Giới thiệu về Mixed model và Random Effects
Chúng ta đã đi qua 3 chặng đường trên hành trình khám phá package GAMLSS, các bạn đã nhận ra gamlss là một công cụ dựng mô hình hồi quy độc đáo, không giống bất cứ package nào khác trong R. Cho đến thời điểm này, chúng ta chỉ mới xét đến thành phần xác định (fixed effect) trong mô hình Gamlss. Trong chặng thứ 4 này, Nhi sẽ giới thiệu với các bạn về thành phần bất định hay ngẫu nhiên (random terms hay random effect) trong một mô hình gọi là hỗn hợp (mixed model).
Loại mô hình này còn được biết dưới nhiều tên gọi khác nhau trong sách vở, như multilevel model, hierarchical model, mixed model, nested model, random effect model, subject specific model, variance component model, random intercept và random slope model, repeated measure ANOVA… (nghe có vẻ đáng sợ phải không các bạn ?). Sự “phong phú” về thuật ngữ này đã khoác lên chủ đề này một màu sắc bí hiểm. Mixed model còn là thử thách với những người không chuyên vì bạn không thể học sâu về nó mà không sử dụng code lập trình (SAS hoặc R).
Điều tồi tệ hơn nữa đó là trong Y văn, tác giả rất hiếm khi mô tả đích danh Mixed model, và gần như không bao giờ trình bày nội dung mô hình mà chỉ nói một cách chung chung là ANOVA hoặc linear model (dù mixed model và random effect là chìa khóa cho hầu hết thử nghiệm lâm sàng, longitudinal study, split-plot và repeated measure design). Ngay cả khi họ dùng mixed model, thì cũng chỉ trình bày fixed effect mà giấu đi phần random trong mô hình. Do đó, không có nhiều sinh viên Y khoa và bác sĩ biết dùng Mixed model và random effect.
Một người học hồi quy tuyến tính chưa thể đạt đến cảnh giới tối cao nếu chưa thực hành qua Mixed model, và như Nhi đã nói, đây là phương pháp quan trọng, nguồn gốc của những phân tích ANOVA trong thử nghiệm lâm sàng.
Tóm lại, GLMM rất quan trọng và cần thiết nếu bạn muốn dùng hồi quy để giải quyết các nghiên cứu lâm sàng, thí nghiệm. GLMM phức tạp, nhưng Nhi sẽ cố gắng trình bày về Mixed model một cách đơn giản nhất có thể, và do chúng ta đang sử dụng R, khoảng cách đến thành công không còn xa bao nhiêu.
Mixed model là một chủ đề rất rộng, có thể trình bày về nó trong cả quyển sách. Nhi giới thiệu một vài tựa sách mà bạn có thể tìm đọc như Mixed Models, Theory and Application with R của Eugene Demidenko, Generalized, linear and Mixed model của Charles E. McCulloch, Applied Mixed models in Medicine của Helen Brown và Robin Prescott, Introduction to Mixed Modelling – Beyond Regression and Analysis of Variance, Analysis of Generalized Linear Mixed Models, Mixed effects models and extensions in Ecology with R của Alain F.Ziir và cộng sự, vân vân… Nhi không có khả năng đi quá sâu vào chi tiết vào chủ đề này chỉ trong 1 bài viết ngắn. Mục tiêu của bài thực hành này do đó chỉ nhắm đến việc giới thiệu một số thuật ngữ, khái niệm chính, và cú pháp của một số hàm trong Gamlss. Để đơn giản và nhất quán, Nhi sẽ sử dụng Generalized linear mixed model (GLMM) như tên gọi của phương pháp.
Một cách cơ bản, mô hình hồi quy tuyến tính cho phép chúng ta ước lượng (và kiểm soát) một phần sự biến thiên của biến kết quả, ta gọi đây là phần xác định hay hiệu ứng xác định (fixed effect). Tuy nhiên, như ta biết, không có mô hình nào hoàn hảo: ngay cả thành phần xác định của một mô hình phù hợp nhất vẫn không cho phép giải thích toàn bộ sự biến thiên của biến kết quả, phần sai số còn dư thường được cho là hệ quả của tính ngẫu nhiên (trong khi chọn mẫu chẳng hạn). Tuy nhiên các bạn có thể xem thành tố ngẫu nhiên này như 1 biến số (yếu tố) và đưa nó vào trong mô hình (dưới dạng xác suất điều kiện), như một cố gắng để giải thích nhiều hơn nữa phần sai số còn dư. Như vậy, hiệu ứng ngẫu nhiên (random effect) là những biến số mang tính bất định, ngẫu nhiên được gắn thêm vào mô hình nguyên thủy ở nhiều vị trí khác nhau (thí dụ random intercept, random slope của 1 predictor), cho phép chúng ta ước lượng được sự biến thiên ngẫu nhiên bên ngoài những quy luật mà mô hình fixed effect đã mô tả.
Một thí dụ đơn giản: thử nghiệm lâm sàng về hiệu quả điều trị của loại thuốc X trên n bệnh nhân, fixed effect ở đây là phân nhóm điều trị (1 nhóm Placebo vs nhóm điều trị), còn random effect có thể xem như là cơ địa của bệnh nhân.
Một trong những dấu hiệu nhận dạng một bài toán cần tới mixed model và random effect, đó là tồn tại sự phân nhóm (cụm) trong cấu trúc dữ liệu. Sự phân cụm này có thể là do chủ ý của thiết kế thí nghiệm hoặc hoàn toàn ngẫu nhiên. Việc nhận dạng cấu trúc phân nhóm này nhiều lúc là do suy luận và giả định của chúng ta. Trong thí dụ nêu trên, ta có thể giả định mỗi bệnh nhân không hẳn là 1 cá thể, nhưng là 1 mẫu đại diện cho 1 nhóm trong quần thể thực tế.
Một số loại random effect đặc biệt
1)Nested effect
Một yếu tố (F) lồng trong yếu tố khác (G), hiệu ứng của G bị giới hạn bên trong một cấp bậc nhất định của G, thí dụ G1 nhưng không hiện diện ở cấp bậc khác như G2, G3…. Thí dụ minh họa, một nghiên cứu khảo sát sự thay đổi thị lực dưới tác động của một điều trị phẫu thuật nhãn khoa. Biến số kết quả được đánh giá riêng cho mắt bên trái và mắt bên phải (yếu tố Side) cho mỗi bệnh nhân, trước và sau khi phẫu thuật, như vậy hiệu ứng ngẫu nhiên của yếu tố “Side” gồm 2 levels: Trái/phải được lồng trong yếu tố “Id của Bệnh nhân”, do đó mô hình được viết dưới dạng:
Outcome ~ Treatment + (1|Id) + (1|Id:Side )
Crossed effect:
Đây là hiệu ứng ngẫu nhiên của các yếu tố giao nhau, hay không có cơ sở để lập luận rằng chúng lồng ghép với nhau. Thí dụ để khảo sát mức độ tương hợp giữa 3 bác sĩ chẩn đoán hình ảnh, mỗi người trong số họ sẽ lần lượt đọc 3 hình ảnh Ct scan của cùng một bệnh nhân được chụp trong 3 lần khác nhau bằng 3 thiết bị khác nhau. Như vậy yếu tố ngẫu nhiên sẽ là crossed effect = (1|Doctor) + (1|Replication) chứ không phải là nested effect = (1|Doctor:Replication) vì mỗi level của replication là như nhau cho cả 3 levels của Doctor.
Multilevel model hay Hierarchical model:
Các thuật ngữ này phổ biến trong khoa học xã hội và kinh tế hơn là Y học, thí dụ thường gặp nhất trong sách vở là nghiên cứu về các chỉ số giáo dục với đơn vị là học sinh với yếu tố nẫu nhiên được phân cấp theo thành phố, trường, lớp … tuy nhiên khái niệm về cấu trúc đa cấp trong mô hình cũng có thể gặp trong nghiên cứu Y khoa và dịch tễ học. Thí dụ cộng đồng dân cư có thể phân cấp thành Thành phố, Quận, phường, tổ, v..v ,random effect có dạng cấu trúc đa cấp cũng tương tự như hiệu ứng lồng ghép (nested) và có thể được viết như:
Điểm thi đại học ~ Giới tính + (1|Thành phố) + (1|Thành phố:Trường) + (1|Thành phố:Trường:Lớp)
Repeated measure và Longitudinal study
Đây là thiết kế nghiên cứu rất phổ biến, khi một kết quả (outcome) được khảo sát nhiều lần (tại nhiều thời điểm khác nhau) trên cùng một đối tượng. Thí dụ: Một thử nghiệm thuốc trong đó 1 biomarker hay đại lượng lâm sàng được khảo sát tại thời điểm t0, sau đó bệnh nhân được phân nhóm điều trị (placebo/thuốc X) và ta lặp lại phép đo mỗi 3 tháng trong vòng 1 năm, như vậy ta có 5 thời điểm Baseline,3,6,9,12. Mô hình với random slope do đó sẽ được viết là: Outcome ~ Baseline*Treatment + Time+ (Treatment |Patient Id)
Thí dụ minh họa: Dataset Epileptic
Epileptic là 1 dataset kinh điển cho mô hình random effect, nó đã được sử dụng bở nhiều tác giả như Thall ,Vail [1990], Breslow và Clayton[1993], Lee và Nelder [1996, 2000]. Đây là một nghiên cứu thử nghiệm lâm sàng trong đó có 2 nhóm bệnh nhân (n=59) mắc bệnh động kinh. Tần suất cơn động kinh được đếm trong mỗi 2 tuần và lặp lại 5 lần. Sau 2 tuần đầu tiên, việc điều trị bắt đầu với Placebo và loại thuốc điều trị cần khảo sát. Sau đó số cơn động kinh được ghi nhận sau mỗi 2 tuần trong 4 lần tái khám.
library(tidyverse)
library(gamlss)
my_theme <- function(base_size =8, base_family = "sans"){
theme_bw(base_size = base_size, base_family = base_family) +
theme(
panel.grid.major = element_line(color = "gray"),
panel.grid.minor = element_blank(),
panel.background = element_rect(fill = NA),
strip.background = element_rect(fill = "#510166", color = NA, size =0.5),
strip.text = element_text(face = "bold", size = 8, color = "white"),
legend.position = "bottom",
legend.justification = "center",
legend.background = element_blank(),
legend.margin = margin(0.5,0.5,0.5,0.5)
)
}
Thăm dò số liệu
Như thường lệ, Nhi sẽ bắt đầu thăm dò số liệu bằng biểu đồ
Ghi chú: kết quả ở thời điểm ban đầu trước điều trị được chuyển sang thang logarithm (lbase) Tuổi của bệnh nhân cũng được chuyển sang thang logarithm
epil=read.csv("https://raw.github.com/vincentarelbundock/Rdatasets/master/csv/MASS/epil.csv")
epil$base=as.factor(epil$base)
epil%>%ggplot(aes(x=y,fill=trt))+
geom_density(alpha=.8)+
geom_histogram(aes(y=..density..,fill=trt),colour="black",binwidth = 3,alpha=0.7,show.legend = F)+
my_theme()+scale_fill_manual(values=c("#ff9c07","#d60a29"))+
facet_wrap(~period,ncol=2)+
ggtitle("Density + Histogram")

epil%>%ggplot(aes(x=trt,y=y,group=factor(lbase)))+
geom_smooth(aes(color=lbase),size=1.2,alpha=0.2,se=F,method="lm")+
my_theme()+
scale_colour_gradient(low="#ffa528",high="#db1154")+
facet_wrap(~period,scales="free")

epil%>%ggplot(aes(x=trt,y=y,group=factor(lage)))+
geom_smooth(aes(color=lage),size=1,alpha=0.5,se=F,method="lm")+
my_theme()+
scale_colour_gradient(low="#ffa528",high="#db1154")+
facet_wrap(~period,scales="free")

epil%>%ggplot(aes(x=period,y=y,group=1))+
geom_path(aes(color=trt),alpha=0.8,size=1)+
geom_point(aes(color=trt,shape=trt),alpha=0.5,size=2)+
my_theme(5)+
scale_color_manual(values=c("#ff9c07","#d60a29"))+
facet_wrap(~subject,scales="free",ncol=7)

epil%>%ggplot(aes(x=period,y=y,group=1))+
geom_path(aes(color=trt),alpha=0.4,size=1)+
geom_point(aes(color=trt,shape=trt),alpha=0.5,size=2)+
my_theme(5)+
scale_color_manual(values=c("#ff9c07","#d60a29"))

epil%>%ggplot(aes(x=period,y=y,group=trt,color=trt))+
geom_smooth(alpha=0.5,se=F,method="glm")+
my_theme()+
scale_color_manual(values=c("#ff9c07","#d60a29"))

epil%>%ggplot(aes(x=period,y=log(y),group=factor(lbase),color=lbase))+
geom_smooth(alpha=0.5,se=F,method="glm")+
my_theme()+
scale_colour_gradient2(low="#f7620c",mid="#d60652",high="#62017a")

epil%>%ggplot(aes(x=period,y=log(y),group=lage,color=lage))+
geom_smooth(alpha=0.5,se=F,method="glm")+
scale_colour_gradient2(high="#aa03ba",low="#fc6e02",mid="#d60652")+
my_theme()

epil%>%ggplot(aes(x=period,y=log(y),group=lbase,color=lbase))+
geom_smooth(alpha=0.5,se=F,method="glm")+
my_theme()+facet_wrap(~trt,ncol=2)+
scale_colour_gradient2(high="#aa03ba",low="#fc6e02",mid="#d60652")

epil%>%ggplot(aes(x=lage,y=log(y),group=trt))+
geom_smooth(aes(color=trt,fill=trt),alpha=0.3,method="lm")+
theme_bw()+
scale_color_manual(values=c("#ff9c07","#d60a29"))+
scale_fill_manual(values=c("#f7b80c","#d60a29"))

epil%>%ggplot(aes(x=lage,y=log(y),group=trt,color=trt))+
geom_point(alpha=0.5)+
geom_smooth(alpha=0.5,se=F,method="glm")+
my_theme()+facet_wrap(~period,scales="free")+
scale_color_manual(values=c("#ff9c07","#d60a29"))

epil%>%ggplot(aes(x=lbase,y=log(y),group=trt,color=trt))+
geom_point(alpha=0.5)+
geom_smooth(alpha=0.5,se=F,method="glm")+
my_theme()+facet_wrap(~period,scales="free")+
scale_color_manual(values=c("#ff9c07","#d60a29"))

Các biểu đồ gợi ý rằng :
Biến kết quả là một số đếm, với dấu hiệu overdispersion nhẹ, nên có thể họ phân phối phù hợp cho nó sẽ là negative binomial
Giá trị baseline có tác động lên giá trị biến kết quả trong quá trình điều trị tại cả 4 thời điểm, nhưng chưa rõ liệu có tương tác giữa nó và hiệu ứng điều trị hay không
Nhìn chung, nhóm điều trị giảm rõ nét tần suất cơn động kinh so với nhóm Placebo, nhưng hiệu ứng này không hằng định ở mọi bệnh nhân
Tuổi bệnh nhân không có ảnh hưởng đáng kể đến kết quả điều trị
Nhi chuyển dạng biến số period thành một contrast variable với 4 cấp bậc giá trị:
epil$visit=(2*epil$period-5)/10
factor(epil$visit)%>%levels(.)
## [1] "-0.3" "-0.1" "0.1" "0.3"
Hàm gamlssNP()
Hàm gamlssNP() có công dụng là mô hình hóa marginal likelihood, bản chất của nó là ước tính tổng quát (hiệu ứng ngẫu nhiên được phân tích trong bản thân algorithm của Gamlss).
Tùy lựa chọn của người dùng, hàm gamlssNP cho phép áp dụng 2 phương pháp:
- Phép cầu phương Gaussian (Quadrature) nhằm ước lượng marginal likelihhod bằng cách thay thế tích phân trong khoảng phân phối chuẩn của random effect bằng phép cộng diện tích của nhiều tứ giác (con số do người dùng quy ước).
Công dụng của phương pháp này là dựng mô hình với Random intercept (1 yếu tố ngẫu nhiên liên tục và có phân phối chuẩn) cho tham số vị trí trung tâm Mu.
- phương pháp phi tham số (non parametric) cho yếu tố ngẫu nhiên rời rạc ; với công dụng là mô hình hóa random intercept hoặc cả random intercept và random slope cho từ 1 đến 4 tham số của 1 họ phân phối (Mu, Sigma, Nu và Tau).
Cú pháp hàm gamlssNP
Hàm gamlssNP được hỗ trợ bởi module gamlss.mx có công dụng:
Mô hình hóa random intercept liên tục, phân phối chuẩn, cho Mu Hay: Dựng mô hình phi tham số với random intercept và random slope cho cả Mu, Sigma, Nu và Tau của 1 họ phân phối tùy chọn.
Tùy chỉnh random = có nội dung là 1 công thức để biểu thị cho random effect, thí dụ random intercept ở cấp độ phân nhóm: random = ~ 1|F
Random slope ở cấp độ phân nhóm: random = ~ X|F
Nếu random effect ở cấp độ quan sát: không cần khai báo cho tùy chỉnh random Tùy chỉnh Mixture : phươn pháp ước lượng:
Phép cầu phương Gaussian: gq, dùng cho random intercept phân phối chuẩn Np= nonparametric random effect models K= số điểm trong phép ước lượng np hay gp Khi muốn mở rộng random effect cho các tham số khác như Sigma, Nu và Tau, ta phải khai báo công thức cho các thành tố này, phần random effect được biểu thị bằng tên 1 matrix gọi là MASS, thí dụ: sigma.fo ~ X+ MASS cho biết MASS nằm ở vị trí Intercept, còn sigma.fo ~ X* MASS cho biết MASS nằm cả ở intercept và slope.
library(gamlss.mx)
epil$subject=as.factor(epil$subject)
m0<-gamlssNP(y~1,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..
## EM algorithm met convergence criteria at iteration 19

## Global deviance trend plotted.
m00<-gamlssNP(y~trt,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..20 ..21 ..22 ..23 ..24 ..25 ..26 ..27 ..28 ..29 ..
## EM algorithm met convergence criteria at iteration 29

## Global deviance trend plotted.
m10<-gamlssNP(y~trt+visit,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..20 ..21 ..22 ..23 ..24 ..25 ..26 ..27 ..28 ..29 ..
## EM algorithm met convergence criteria at iteration 29

## Global deviance trend plotted.
m11<-gamlssNP(y~trt+lbase+visit,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..
## EM algorithm met convergence criteria at iteration 14

## Global deviance trend plotted.
m12<-gamlssNP(y~trt*lbase+visit,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..
## EM algorithm met convergence criteria at iteration 13

## Global deviance trend plotted.
m13<-gamlssNP(y~trt*lbase+visit+lage,
random=~1|subject,
K=15,
mixture="gq",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..
## EM algorithm met convergence criteria at iteration 12

## Global deviance trend plotted.
GAIC(m0,m00,m10,m11,m12,m13,k=log(nrow(epil)))
## df AIC
## m11 6 1284.977
## m12 7 1289.034
## m13 8 1292.597
## m0 3 1329.775
## m00 4 1331.816
## m10 5 1334.508
LR.test(m11,m12)
## Likelihood Ratio Test for nested GAMLSS models.
## (No check whether the models are nested is performed).
##
## Null model: deviance= 1252.194 with 6 deg. of freedom
## Altenative model: deviance= 1250.788 with 7 deg. of freedom
##
## LRT = 1.406041 with 1 deg. of freedom and p-value= 0.2357148
LR.test(m10,m11)
## Likelihood Ratio Test for nested GAMLSS models.
## (No check whether the models are nested is performed).
##
## Null model: deviance= 1307.189 with 5 deg. of freedom
## Altenative model: deviance= 1252.194 with 6 deg. of freedom
##
## LRT = 54.995 with 1 deg. of freedom and p-value= 1.207923e-13
summary(m11)
## Warning in summary.gamlss(m11): summary: vcov has failed, option qr is used instead
## ******************************************************************
## Family: "NBI Mixture with NO"
##
## Call:
## gamlssNP(formula = y ~ trt + lbase + visit, random = ~1 | subject,
## family = NBI, data = epil, K = 15, mixture = "gq")
##
## Fitting method: RS()
## Warning in summary.gamlss(m11): observations with zero weight not used for
## calculating dispersion
## ------------------------------------------------------------------
## Mu link function: log
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.81816 0.05329 34.119 < 2e-16 ***
## trtprogabide -0.32936 0.07393 -4.455 8.64e-06 ***
## lbase 1.01523 0.04878 20.813 < 2e-16 ***
## visit -0.27334 0.16426 -1.664 0.0962 .
## z 0.49906 0.03785 13.184 < 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) -2.0228 0.1753 -11.54 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## No. of observations in the fit: 3540
## Degrees of Freedom for the fit: 6
## Residual Deg. of Freedom: 230
## at cycle: 1
##
## Global Deviance: 1252.194
## AIC: 1264.194
## SBC: 1284.977
## ******************************************************************
#method np
m20<-gamlssNP(y~trt+lbase+visit,
random=~1|subject,
K=15,
mixture="np",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..20 ..21 ..22 ..23 ..24 ..25 ..26 ..27 ..28 ..29 ..30 ..31 ..32 ..33 ..
## 34 ..35 ..36 ..37 ..38 ..39 ..40 ..41 ..
## EM algorithm met convergence criteria at iteration 41
## Global deviance trend plotted.

## EM Trajectories plotted.
m21<-gamlssNP(y~trt*lbase+visit,
random=~1|subject,
K=15,
mixture="np",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..20 ..21 ..22 ..23 ..24 ..25 ..26 ..27 ..28 ..29 ..30 ..31 ..32 ..33 ..
## 34 ..35 ..36 ..37 ..38 ..39 ..40 ..41 ..42 ..43 ..44 ..45 ..46 ..47 ..48 ..49 ..50 ..
## 51 ..52 ..53 ..54 ..
## EM algorithm met convergence criteria at iteration 54
## Global deviance trend plotted.

## EM Trajectories plotted.
m22<-gamlssNP(y~trt*lbase+visit,
random=~trt|subject,
K=20,
mixture="np",
data=epil,
family=NBI)
## 1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..10 ..11 ..12 ..13 ..14 ..15 ..16 ..
## 17 ..18 ..19 ..20 ..21 ..22 ..23 ..24 ..25 ..26 ..27 ..28 ..29 ..30 ..31 ..32 ..33 ..
## 34 ..35 ..36 ..37 ..38 ..39 ..40 ..41 ..42 ..43 ..44 ..45 ..46 ..47 ..48 ..49 ..50 ..
## 51 ..52 ..53 ..54 ..55 ..56 ..57 ..58 ..59 ..60 ..
## EM algorithm met convergence criteria at iteration 60
## Global deviance trend plotted.

## EM Trajectories plotted.
GAIC(m20,m21,m22,k=log(nrow(epil)))
## df AIC
## m20 33 1422.983
## m21 34 1428.355
## m22 63 1580.233
plotMP(m22$mass.points[,1], m22$mass.points[,2], m22$prob,theta=50,
phi=30,col = "gold")

summary(m20)
## Warning in summary.gamlss(m20): summary: vcov has failed, option qr is used instead
## ******************************************************************
## Family: c("NBI Mixture with NP", "Negative Binomial type I Mixture with NP" )
##
## Call:
## gamlssNP(formula = y ~ trt + lbase + visit, random = ~1 | subject,
## family = NBI, data = epil, K = 15, mixture = "np")
##
## Fitting method: RS()
## Warning in summary.gamlss(m20): observations with zero weight not used for
## calculating dispersion
## ------------------------------------------------------------------
## Mu link function: log
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.079e-01 2.325e+02 0.003 0.997227
## trtprogabide -2.805e-01 7.307e-02 -3.838 0.000126 ***
## lbase 9.259e-01 4.838e-02 19.140 < 2e-16 ***
## visit -2.733e-01 1.605e-01 -1.703 0.088661 .
## MASS2 5.383e-05 2.327e+02 0.000 1.000000
## MASS3 6.408e-05 2.325e+02 0.000 1.000000
## MASS4 7.265e-05 2.325e+02 0.000 1.000000
## MASS5 9.060e-05 2.325e+02 0.000 1.000000
## MASS6 9.373e-01 2.325e+02 0.004 0.996783
## MASS7 9.577e-01 2.325e+02 0.004 0.996713
## MASS8 9.601e-01 2.325e+02 0.004 0.996705
## MASS9 1.456e+00 2.325e+02 0.006 0.995002
## MASS10 2.015e+00 2.325e+02 0.009 0.993086
## MASS11 2.015e+00 2.325e+02 0.009 0.993086
## MASS12 2.015e+00 2.325e+02 0.009 0.993086
## MASS13 2.015e+00 2.325e+02 0.009 0.993086
## MASS14 2.015e+00 2.331e+02 0.009 0.993105
## MASS15 2.015e+00 5.749e+02 0.004 0.997204
## ---
## 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) -2.1224 0.1929 -11 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## No. of observations in the fit: 3540
## Degrees of Freedom for the fit: 33
## Residual Deg. of Freedom: 203
## at cycle: 1
##
## Global Deviance: 1242.676
## AIC: 1308.676
## SBC: 1422.983
## ******************************************************************
Hàm random() và re()
Khác với hàm gamlssNP(), hàm random() và re( ) sử dụng phép ước lượng likelihood cục bộ (local) còn gọi là phương pháp penalized quasi likelihood (PQL) để ước tính joint likelihood, lúc này sự biến thiên biến số kết quả sẽ được mô hình hóa thông qua xác suất có điều kiện tùy thuộc vào random effect.
Hàm random() trong gamlss là phiên bản của hàm cùng tên trong package gam của Tishibrani được cải biên bởi Rigby và Stasinopoulos để dùng cho package gamlss. Nó cho phép mô hình hóa random intercept có phân phối chuẩn.
Hàm re() là một cánh cửa liên thông với hàm lme() trong package nlme, một công cụ nổi tiếng cho mixed model (Pinheiro và Bates, 2000). Nó sẽ gọi package nlme() và kết hợp package này và gamlss để dựng mixed model với 3 dạng : random intercept và random slope, multilevel modelling và repeated measurement.
#Random function
m30<-gamlss(y~trt+lbase+visit+random(subject),
data=epil,
family=NBI,
trace=FALSE)
m31<-gamlss(y~trt*lbase+visit+random(subject),
sigma.fo=~random(subject),
data=epil,
family=NBI,
trace=FALSE)
m32<-gamlss(y~trt*lbase+visit+lage+random(subject),
sigma.fo=~random(subject),
data=epil,
family=NBI,
trace=FALSE)
GAIC(m30,m31,m32,k=log(nrow(epil)))
## df AIC
## m30 48.25053 1390.694
## m31 65.63829 1424.132
## m32 65.52979 1425.443
summary(m30)
## ******************************************************************
## Family: c("NBI", "Negative Binomial type I")
##
## Call: gamlss(formula = y ~ trt + lbase + visit + random(subject),
## family = NBI, data = epil, trace = FALSE)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: log
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.82477 0.04801 38.007 < 2e-16 ***
## trtprogabide -0.32357 0.06576 -4.920 1.88e-06 ***
## lbase 0.99605 0.04322 23.045 < 2e-16 ***
## visit -0.27075 0.14744 -1.836 0.0679 .
## ---
## 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) -2.4855 0.2426 -10.24 <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: 236
## Degrees of Freedom for the fit: 48.25053
## Residual Deg. of Freedom: 187.7495
## at cycle: 10
##
## Global Deviance: 1127.062
## AIC: 1223.563
## SBC: 1390.694
## ******************************************************************
rdf=data.frame(mu=as.vector(m30$mu.s),
muL=as.vector(m30$mu.s-(sqrt(m30$mu.var)/sqrt(236))),
muU=as.vector(m30$mu.s+(sqrt(m30$mu.var)/sqrt(236))),
Id=epil$subject)
rdf%>%mutate(.,Positive=ifelse(rdf$mu>0,"Positive","Negative"))%>%
ggplot(aes(x=as.factor(Id),color=Positive))+
geom_errorbar(aes(ymin=muL,ymax=muU))+
geom_point(aes(y=mu))+
geom_hline(yintercept = 0,color="blue",linetype=2)+
theme_bw(5)+
coord_flip()+
scale_x_discrete("Idx")+
scale_color_manual(values=c("#ff9c07","#d60a29"))

Hàm re() mang lại cho gamlss khả năng tương đương với package nlme(), tức là có thể mô hình hóa những random effect phức tạp mà không cần xét đến giả định phân phối chuẩn. Cách sử dụng hàm re cũn tương tự như package nlme, nếu bạn đã quen thuộc với mixed model thì sẽ thấy nó rất dễ dàng. Hàm re có thể áp dụng cho bất kì tham số phân phối nào, nó có những tùy chỉnh như sau;
fixed = khai báo nội dung cho công thức của fixed effect, nhưng không cần khai báo biến kết quả
- random = công thức cho random effects
- x1 + … xn |f1/… / fm
- Với f1/…/fm là nested structure (khối), khi random effect nằm ở cấp độ từng quan sát, phần sau không cần
- x1 + … xn |f
Một danh sách công thức:
List(f1=pdDiag(~ x1), f2=pdSymm(~ x2)) = random intercept và slope cho x1 ở cấp độ factor f1, với covariance structure pdDiag
pdSymm= giá trị mặc định, cấu trúc matrix xác định dương và đối xứng
pdBlocked: cấu trúc dạng khối – phân giác
random=list(f1=pdBlocked(list(pdIndent(1),pdIdent(f2-1))))
pdCompSymm: cấu trúc đối xứng tổ hợp, thích hợp cho nested random effect (thí dụ factor f2 nested trong factor f1:
random=list(f1=pdCopmSymm(~f2-1))
pdDiag: cấu trúc matrix phân giác: giả định tính độc lập giữa random effects random=pdDiag(~x1+x2)
trong đó random slope cho x1 và x2 và intercept giả định như độc lập với nhau pdIdent: cấu trúc matrix Identity
khi cấp bậc của factor f1 có variance như nhau: random=pdIdent(~f1-1)
Correlation: xác định cấu trúc tương quan bên trong mỗi khối (nhóm) hay error variation
Đây là nơi chúng ta có thể đưa vào những cấu trúc tương quan đặc biệt của time series hay spatial structures
Về time series ta có:
corCompSymm: cấu trúc đối xứng tổ hợp, tương ứng với tương quan đồng nhất (uniform)
corAR1: autoregressive process cấp 1, hàm correlation = rho^k với k=0,1,2…
corCAR1: continuous autoregressive process AR1 process cho 1 hiệp biến số thời gian liên tục, rho^s với s > =0
corARMA= autoregressive moving average process, với arbitrary orders cho thành phần autoregressive và moving average (ARMA(p,q)) tương ứng với sự kết hợp giữa p autoregressive parameters và q moving average parameters
corExp: exponential spatial correlation
corGaus: Gaussian spatial correlation
corLin: linear spatial correlation;
corRatio: rational quadratic spatial correlation
corSpher: spherical spatial correlation
method =”ML” hay “REML”
#RE function
m40<-gamlss(y~trt+lbase+visit+
re(random=~1|subject),
family=NBI,
data=epil,
trace=FALSE)
m41<-gamlss(y~trt*lbase+visit+
re(random=~1|subject),
family=NBI,
data=epil,
trace=FALSE)
m42<-gamlss(y~trt*lbase+visit+
re(random=~1|subject),
sigma.fo=~re(random=~1|subject),
family=NBI,
data=epil,
trace=FALSE)
GAIC(m40,m41,m42,k=log(nrow(epil)))
## df AIC
## m40 48.25031 1390.693
## m41 48.75377 1393.872
## m42 65.58082 1423.796
rdf=data.frame(mu=as.vector(m42$mu.s),
sigma=as.vector(m42$sigma.s),
Id=epil$subject)
rdf%>%gather(mu:sigma,key="Parameter",value="Effect")%>%
mutate(.,Positive=ifelse(Effect>0,"Positive","Negative"))%>%
ggplot(aes(x=as.factor(Id),y=Effect,color=Positive))+
geom_point()+
geom_hline(yintercept = 0,color="blue",linetype=2)+
theme_bw(5)+
coord_flip()+
scale_x_discrete("Subjects")+
facet_wrap(~Parameter,ncol=2)+
scale_color_manual(values=c("#ff9c07","#d60a29"))

Marginal effects
term.plot(m40, pages=1)

term.plot(m30, pages=1)

epil%>%mutate(.,predict=predict(m30,type="response"))%>%
ggplot(aes(x=trt,y=log(predict)))+
geom_boxplot(aes(fill=trt),alpha=0.7)+
my_theme(5)+facet_wrap(~period,ncol=1)+coord_flip()+
scale_fill_manual(values=c("#ff9c07","#d60a29"))

Kết luận
Nhận xét: Nhi vừa trình bày một cách khái quát công dụng của 4 hàm trong gamlss cho phép mô hình hóa random effect hay thành phần ngẫu nhiên, bất định. Có thể thấy tuy gamlss đã hấp thu nội dung từ nhiều package có trước nó nhưng về mọi phương diện, quy trình dựng mixed model trong gamlss không tương thích với những công cụ kinh điển như nlme hay lme4. Tức là ta không thể trích xuất kết quả và suy diễn thống kê dễ dàng như output của package lme4 hay nlme. Đây là nhược điểm của gamlss.
Vậy khi nào ta nên dùng gamlss: chỉ khi bạn gặp vấn đề khó về phân phối (vì gamlss cho phép mô hình hóa nhiều họ phân phối hơn bất cứ package nào khác). Thực ra Nhi luôn dùng lme và nlme cho mixed model nhưng chỉ mới dùng đến gamlss 2 lần duy nhất, một lần là cho 1 longitudinal study cho 1 em bác sĩ nội trú bên VN vì biến số kết quả của em ấy có phân phối Gamma; một lần khác là cho một nghiên cứu lâm sàng với 10 biến số kết quả là count data có phân phối zero inflated negative binomial.
Xin cảm ơn và hẹn gặp lại.
LS0tDQp0aXRsZTogIlPhu60gZOG7pW5nIHBhY2thZ2UgR2FtbHNzIg0Kc3VidGl0bGU6ICJCw6BpIDQ6IFRow6BuaCBwaOG6p24gYuG6pXQgxJHhu4tuaCAoUmFuZG9tIGVmZmVjdHMpICINCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjAzIFRow6FuZyA4IDIwMTciDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmxpYnJhcnkoZ2FtbHNzKQ0KDQpuQzwtZGV0ZWN0Q29yZXMoKQ0KYGBgDQoNCiFbXShnYW1sc3M0LnBuZykNCg0KIyBHaeG7m2kgdGhp4buHdSB24buBIE1peGVkIG1vZGVsIHbDoCBSYW5kb20gRWZmZWN0cw0KDQpDaMO6bmcgdGEgxJHDoyDEkWkgcXVhIDMgY2jhurduZyDEkcaw4budbmcgdHLDqm4gaMOgbmggdHLDrG5oIGtow6FtIHBow6EgcGFja2FnZSBHQU1MU1MsIGPDoWMgYuG6oW4gxJHDoyBuaOG6rW4gcmEgZ2FtbHNzIGzDoCBt4buZdCBjw7RuZyBj4bulIGThu7FuZyBtw7QgaMOsbmggaOG7k2kgcXV5IMSR4buZYyDEkcOhbywga2jDtG5nIGdp4buRbmcgYuG6pXQgY+G7qSBwYWNrYWdlIG7DoG8ga2jDoWMgdHJvbmcgUi4gQ2hvIMSR4bq/biB0aOG7nWkgxJFp4buDbSBuw6B5LCBjaMO6bmcgdGEgY2jhu4kgbeG7m2kgeMOpdCDEkeG6v24gdGjDoG5oIHBo4bqnbiB4w6FjIMSR4buLbmggKGZpeGVkIGVmZmVjdCkgdHJvbmcgbcO0IGjDrG5oIEdhbWxzcy4gVHJvbmcgY2jhurduZyB0aOG7qSA0IG7DoHksIE5oaSBz4bq9IGdp4bubaSB0aGnhu4d1IHbhu5tpIGPDoWMgYuG6oW4gduG7gSB0aMOgbmggcGjhuqduIGLhuqV0IMSR4buLbmggaGF5IG5n4bqrdSBuaGnDqm4gKHJhbmRvbSB0ZXJtcyBoYXkgcmFuZG9tIGVmZmVjdCkgdHJvbmcgbeG7mXQgbcO0IGjDrG5oIGfhu41pIGzDoCBo4buXbiBo4bujcCAobWl4ZWQgbW9kZWwpLiANCg0KTG/huqFpIG3DtCBow6xuaCBuw6B5IGPDsm4gxJHGsOG7o2MgYmnhur90IGTGsOG7m2kgbmhp4buBdSB0w6puIGfhu41pIGtow6FjIG5oYXUgdHJvbmcgc8OhY2ggduG7nywgbmjGsCBtdWx0aWxldmVsIG1vZGVsLCBoaWVyYXJjaGljYWwgbW9kZWwsIG1peGVkIG1vZGVsLCBuZXN0ZWQgbW9kZWwsIHJhbmRvbSBlZmZlY3QgbW9kZWwsIHN1YmplY3Qgc3BlY2lmaWMgbW9kZWwsIHZhcmlhbmNlIGNvbXBvbmVudCBtb2RlbCwgcmFuZG9tIGludGVyY2VwdCB2w6AgcmFuZG9tIHNsb3BlIG1vZGVsLCByZXBlYXRlZCBtZWFzdXJlIEFOT1ZB4oCmIChuZ2hlIGPDsyB24bq7IMSRw6FuZyBz4bujIHBo4bqjaSBraMO0bmcgY8OhYyBi4bqhbiA/KS4gU+G7sSDigJxwaG9uZyBwaMO64oCdIHbhu4EgdGh14bqtdCBuZ+G7ryBuw6B5IMSRw6Mga2hvw6FjIGzDqm4gY2jhu6cgxJHhu4EgbsOgeSBt4buZdCBtw6B1IHPhuq9jIGLDrSBoaeG7g20uIE1peGVkIG1vZGVsIGPDsm4gbMOgIHRo4butIHRow6FjaCB24bubaSBuaOG7r25nIG5nxrDhu51pIGtow7RuZyBjaHV5w6puIHbDrCBi4bqhbiBraMO0bmcgdGjhu4MgaOG7jWMgc8OidSB24buBIG7DsyBtw6Aga2jDtG5nIHPhu60gZOG7pW5nIGNvZGUgbOG6rXAgdHLDrG5oIChTQVMgaG/hurdjIFIpLg0KDQrEkGnhu4F1IHThu5NpIHThu4cgaMahbiBu4buvYSDEkcOzIGzDoCB0cm9uZyBZIHbEg24sIHTDoWMgZ2nhuqMgcuG6pXQgaGnhur9tIGtoaSBtw7QgdOG6oyDEkcOtY2ggZGFuaCBNaXhlZCBtb2RlbCwgdsOgIGfhuqduIG5oxrAga2jDtG5nIGJhbyBnaeG7nSB0csOsbmggYsOgeSBu4buZaSBkdW5nIG3DtCBow6xuaCBtw6AgY2jhu4kgbsOzaSBt4buZdCBjw6FjaCBjaHVuZyBjaHVuZyBsw6AgQU5PVkEgaG/hurdjIGxpbmVhciBtb2RlbCAoZMO5IG1peGVkIG1vZGVsIHbDoCByYW5kb20gZWZmZWN0IGzDoCBjaMOsYSBraMOzYSBjaG8gaOG6p3UgaOG6v3QgdGjhu60gbmdoaeG7h20gbMOibSBzw6BuZywgbG9uZ2l0dWRpbmFsIHN0dWR5LCBzcGxpdC1wbG90IHbDoCByZXBlYXRlZCBtZWFzdXJlIGRlc2lnbikuIE5nYXkgY+G6oyBraGkgaOG7jSBkw7luZyBtaXhlZCBtb2RlbCwgdGjDrCBjxaluZyBjaOG7iSB0csOsbmggYsOgeSBmaXhlZCBlZmZlY3QgbcOgIGdp4bqldSDEkWkgcGjhuqduIHJhbmRvbSB0cm9uZyBtw7QgaMOsbmguIERvIMSRw7MsIGtow7RuZyBjw7Mgbmhp4buBdSBzaW5oIHZpw6puIFkga2hvYSB2w6AgYsOhYyBzxKkgYmnhur90IGTDuW5nIE1peGVkIG1vZGVsIHbDoCByYW5kb20gZWZmZWN0LiANCg0KTeG7mXQgbmfGsOG7nWkgaOG7jWMgaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggY2jGsGEgdGjhu4MgxJHhuqF0IMSR4bq/biBj4bqjbmggZ2nhu5tpIHThu5FpIGNhbyBu4bq/dSBjaMawYSB0aOG7sWMgaMOgbmggcXVhIE1peGVkIG1vZGVsLCB2w6AgbmjGsCBOaGkgxJHDoyBuw7NpLCDEkcOieSBsw6AgcGjGsMahbmcgcGjDoXAgcXVhbiB0cuG7jW5nLCBuZ3Xhu5NuIGfhu5FjIGPhu6dhIG5o4buvbmcgcGjDom4gdMOtY2ggQU5PVkEgdHJvbmcgdGjhu60gbmdoaeG7h20gbMOibSBzw6BuZy4NCg0KVMOzbSBs4bqhaSwgR0xNTSBy4bqldCBxdWFuIHRy4buNbmcgdsOgIGPhuqduIHRoaeG6v3QgbuG6v3UgYuG6oW4gbXXhu5FuIGTDuW5nIGjhu5NpIHF1eSDEkeG7gyBnaeG6o2kgcXV54bq/dCBjw6FjIG5naGnDqm4gY+G7qXUgbMOibSBzw6BuZywgdGjDrSBuZ2hp4buHbS4gR0xNTSBwaOG7qWMgdOG6oXAsIG5oxrBuZyBOaGkgc+G6vSBj4buRIGfhuq9uZyB0csOsbmggYsOgeSB24buBIE1peGVkIG1vZGVsIG3hu5l0IGPDoWNoIMSRxqFuIGdp4bqjbiBuaOG6pXQgY8OzIHRo4buDLCB2w6AgZG8gY2jDum5nIHRhIMSRYW5nIHPhu60gZOG7pW5nIFIsIGtob+G6o25nIGPDoWNoIMSR4bq/biB0aMOgbmggY8O0bmcga2jDtG5nIGPDsm4geGEgYmFvIG5oacOqdS4gDQoNCk1peGVkIG1vZGVsIGzDoCBt4buZdCBjaOG7pyDEkeG7gSBy4bqldCBy4buZbmcsIGPDsyB0aOG7gyB0csOsbmggYsOgeSB24buBIG7DsyB0cm9uZyBj4bqjIHF1eeG7g24gc8OhY2guIE5oaSBnaeG7m2kgdGhp4buHdSBt4buZdCB2w6BpIHThu7FhIHPDoWNoIG3DoCBi4bqhbiBjw7MgdGjhu4MgdMOsbSDEkeG7jWMgbmjGsCBNaXhlZCBNb2RlbHMsIFRoZW9yeSBhbmQgQXBwbGljYXRpb24gd2l0aCBSIGPhu6dhIEV1Z2VuZSBEZW1pZGVua28sIEdlbmVyYWxpemVkLCBsaW5lYXIgYW5kIE1peGVkIG1vZGVsIGPhu6dhIENoYXJsZXMgRS4gTWNDdWxsb2NoLCBBcHBsaWVkIE1peGVkIG1vZGVscyBpbiBNZWRpY2luZSBj4bunYSBIZWxlbiBCcm93biB2w6AgUm9iaW4gUHJlc2NvdHQsIEludHJvZHVjdGlvbiB0byBNaXhlZCBNb2RlbGxpbmcg4oCTIEJleW9uZCBSZWdyZXNzaW9uIGFuZCBBbmFseXNpcyBvZiBWYXJpYW5jZSwgQW5hbHlzaXMgb2YgR2VuZXJhbGl6ZWQgTGluZWFyIE1peGVkIE1vZGVscywgTWl4ZWQgZWZmZWN0cyBtb2RlbHMgYW5kIGV4dGVuc2lvbnMgaW4gRWNvbG9neSB3aXRoIFIgY+G7p2EgQWxhaW4gRi5aaWlyIHbDoCBj4buZbmcgc+G7sSwgdsOibiB2w6Ju4oCmIE5oaSBraMO0bmcgY8OzIGto4bqjIG7Eg25nIMSRaSBxdcOhIHPDonUgdsOgbyBjaGkgdGnhur90IHbDoG8gY2jhu6cgxJHhu4EgbsOgeSBjaOG7iSB0cm9uZyAxIGLDoGkgdmnhur90IG5n4bqvbi4gTeG7pWMgdGnDqnUgY+G7p2EgYsOgaSB0aOG7sWMgaMOgbmggbsOgeSBkbyDEkcOzIGNo4buJIG5o4bqvbSDEkeG6v24gdmnhu4djIGdp4bubaSB0aGnhu4d1IG3hu5l0IHPhu5EgdGh14bqtdCBuZ+G7rywga2jDoWkgbmnhu4dtIGNow61uaCwgdsOgIGPDuiBwaMOhcCBj4bunYSBt4buZdCBz4buRIGjDoG0gdHJvbmcgR2FtbHNzLiDEkOG7gyDEkcahbiBnaeG6o24gdsOgIG5o4bqldCBxdcOhbiwgTmhpIHPhur0gc+G7rSBk4bulbmcgR2VuZXJhbGl6ZWQgbGluZWFyIG1peGVkIG1vZGVsIChHTE1NKSBuaMawIHTDqm4gZ+G7jWkgY+G7p2EgcGjGsMahbmcgcGjDoXAuIA0KDQpN4buZdCBjw6FjaCBjxqEgYuG6o24sIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCBjaG8gcGjDqXAgY2jDum5nIHRhIMaw4bubYyBsxrDhu6NuZyAodsOgIGtp4buDbSBzb8OhdCkgbeG7mXQgcGjhuqduIHPhu7EgYmnhur9uIHRoacOqbiBj4bunYSBiaeG6v24ga+G6v3QgcXXhuqMsIHRhIGfhu41pIMSRw6J5IGzDoCBwaOG6p24geMOhYyDEkeG7i25oIGhheSBoaeG7h3Ug4bupbmcgeMOhYyDEkeG7i25oIChmaXhlZCBlZmZlY3QpLiBUdXkgbmhpw6puLCBuaMawIHRhIGJp4bq/dCwga2jDtG5nIGPDsyBtw7QgaMOsbmggbsOgbyBob8OgbiBo4bqjbzogbmdheSBj4bqjIHRow6BuaCBwaOG6p24geMOhYyDEkeG7i25oIGPhu6dhIG3hu5l0IG3DtCBow6xuaCBwaMO5IGjhu6NwIG5o4bqldCB24bqrbiBraMO0bmcgY2hvIHBow6lwIGdp4bqjaSB0aMOtY2ggdG/DoG4gYuG7mSBz4buxIGJp4bq/biB0aGnDqm4gY+G7p2EgYmnhur9uIGvhur90IHF14bqjLCBwaOG6p24gc2FpIHPhu5EgY8OybiBkxrAgdGjGsOG7nW5nIMSRxrDhu6NjIGNobyBsw6AgaOG7hyBxdeG6oyBj4bunYSB0w61uaCBuZ+G6q3Ugbmhpw6puICh0cm9uZyBraGkgY2jhu41uIG3huqt1IGNo4bqzbmcgaOG6oW4pLiBUdXkgbmhpw6puIGPDoWMgYuG6oW4gY8OzIHRo4buDIHhlbSB0aMOgbmggdOG7kSBuZ+G6q3Ugbmhpw6puIG7DoHkgbmjGsCAxIGJp4bq/biBz4buRICh54bq/dSB04buRKSB2w6AgxJHGsGEgbsOzIHbDoG8gdHJvbmcgbcO0IGjDrG5oIChkxrDhu5tpIGThuqFuZyB4w6FjIHN14bqldCDEkWnhu4F1IGtp4buHbiksIG5oxrAgbeG7mXQgY+G7kSBn4bqvbmcgxJHhu4MgZ2nhuqNpIHRow61jaCBuaGnhu4F1IGjGoW4gbuG7r2EgcGjhuqduIHNhaSBz4buRIGPDsm4gZMawLiBOaMawIHbhuq15LCBoaeG7h3Ug4bupbmcgbmfhuqt1IG5oacOqbiAocmFuZG9tIGVmZmVjdCkgbMOgIG5o4buvbmcgYmnhur9uIHPhu5EgbWFuZyB0w61uaCBi4bqldCDEkeG7i25oLCBuZ+G6q3Ugbmhpw6puIMSRxrDhu6NjIGfhuq9uIHRow6ptIHbDoG8gbcO0IGjDrG5oIG5ndXnDqm4gdGjhu6d5IOG7nyBuaGnhu4F1IHbhu4sgdHLDrSBraMOhYyBuaGF1ICh0aMOtIGThu6UgcmFuZG9tIGludGVyY2VwdCwgcmFuZG9tIHNsb3BlIGPhu6dhIDEgcHJlZGljdG9yKSwgY2hvIHBow6lwICBjaMO6bmcgdGEgxrDhu5tjIGzGsOG7o25nIMSRxrDhu6NjIHPhu7EgYmnhur9uIHRoacOqbiBuZ+G6q3Ugbmhpw6puIGLDqm4gbmdvw6BpIG5o4buvbmcgcXV5IGx14bqtdCBtw6AgbcO0IGjDrG5oIGZpeGVkIGVmZmVjdCDEkcOjIG3DtCB04bqjLiANCg0KTeG7mXQgdGjDrSBk4bulIMSRxqFuIGdp4bqjbjogdGjhu60gbmdoaeG7h20gbMOibSBzw6BuZyB24buBIGhp4buHdSBxdeG6oyDEkWnhu4F1IHRy4buLIGPhu6dhIGxv4bqhaSB0aHXhu5FjIFggdHLDqm4gbiBi4buHbmggbmjDom4sIGZpeGVkIGVmZmVjdCDhu58gxJHDonkgbMOgIHBow6JuIG5ow7NtIMSRaeG7gXUgdHLhu4sgKDEgbmjDs20gUGxhY2VibyB2cyBuaMOzbSDEkWnhu4F1IHRy4buLKSwgY8OybiByYW5kb20gZWZmZWN0IGPDsyB0aOG7gyB4ZW0gbmjGsCBsw6AgY8ahIMSR4buLYSBj4bunYSBi4buHbmggbmjDom4uIA0KDQpN4buZdCB0cm9uZyBuaOG7r25nIGThuqV1IGhp4buHdSBuaOG6rW4gZOG6oW5nIG3hu5l0IGLDoGkgdG/DoW4gY+G6p24gdOG7m2kgbWl4ZWQgbW9kZWwgdsOgIHJhbmRvbSBlZmZlY3QsIMSRw7MgbMOgIHThu5NuIHThuqFpIHPhu7EgcGjDom4gbmjDs20gKGPhu6VtKSB0cm9uZyBj4bqldSB0csO6YyBk4buvIGxp4buHdS4gU+G7sSBwaMOibiBj4bulbSBuw6B5IGPDsyB0aOG7gyBsw6AgZG8gY2jhu6cgw70gY+G7p2EgdGhp4bq/dCBr4bq/IHRow60gbmdoaeG7h20gaG/hurdjIGhvw6BuIHRvw6BuIG5n4bqrdSBuaGnDqm4uIFZp4buHYyBuaOG6rW4gZOG6oW5nIGPhuqV1IHRyw7pjIHBow6JuIG5ow7NtIG7DoHkgbmhp4buBdSBsw7pjIGzDoCBkbyBzdXkgbHXhuq1uIHbDoCBnaeG6oyDEkeG7i25oIGPhu6dhIGNow7puZyB0YS4gVHJvbmcgdGjDrSBk4bulIG7DqnUgdHLDqm4sIHRhIGPDsyB0aOG7gyBnaeG6oyDEkeG7i25oIG3hu5dpIGLhu4duaCBuaMOibiBraMO0bmcgaOG6s24gbMOgIDEgY8OhIHRo4buDLCBuaMawbmcgbMOgIDEgbeG6q3UgxJHhuqFpIGRp4buHbiBjaG8gMSBuaMOzbSB0cm9uZyBxdeG6p24gdGjhu4MgdGjhu7FjIHThur8uIA0KDQoqKk3hu5l0IHPhu5EgbG/huqFpIHJhbmRvbSBlZmZlY3QgxJHhurdjIGJp4buHdCoqDQoNCioqMSlOZXN0ZWQgZWZmZWN0ICoqDQoNCk3hu5l0IHnhur91IHThu5EgKEYpIGzhu5NuZyB0cm9uZyB54bq/dSB04buRIGtow6FjIChHKSwgaGnhu4d1IOG7qW5nIGPhu6dhIEcgYuG7iyBnaeG7m2kgaOG6oW4gYsOqbiB0cm9uZyBt4buZdCBj4bqlcCBi4bqtYyBuaOG6pXQgxJHhu4tuaCBj4bunYSBHLCB0aMOtIGThu6UgRzEgbmjGsG5nIGtow7RuZyBoaeG7h24gZGnhu4duIOG7nyBj4bqlcCBi4bqtYyBraMOhYyBuaMawIEcyLCBHM+KApi4gVGjDrSBk4bulIG1pbmggaOG7jWEsIG3hu5l0IG5naGnDqm4gY+G7qXUga2jhuqNvIHPDoXQgc+G7sSB0aGF5IMSR4buVaSB0aOG7iyBs4buxYyBkxrDhu5tpIHTDoWMgxJHhu5luZyBj4bunYSBt4buZdCDEkWnhu4F1IHRy4buLIHBo4bqrdSB0aHXhuq10IG5ow6NuIGtob2EuIEJp4bq/biBz4buRIGvhur90IHF14bqjIMSRxrDhu6NjIMSRw6FuaCBnacOhIHJpw6puZyBjaG8gbeG6r3QgYsOqbiB0csOhaSB2w6AgbeG6r3QgYsOqbiBwaOG6o2kgKHnhur91IHThu5EgU2lkZSkgY2hvIG3hu5dpIGLhu4duaCBuaMOibiwgdHLGsOG7m2MgdsOgIHNhdSBraGkgcGjhuqt1IHRodeG6rXQsIG5oxrAgduG6rXkgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4gY+G7p2EgeeG6v3UgdOG7kSDigJxTaWRl4oCdIGfhu5NtIDIgbGV2ZWxzOiBUcsOhaS9waOG6o2kgxJHGsOG7o2MgbOG7k25nIHRyb25nIHnhur91IHThu5Eg4oCcSWQgY+G7p2EgQuG7h25oIG5ow6Ju4oCdLCBkbyDEkcOzIG3DtCBow6xuaCDEkcaw4bujYyB2aeG6v3QgZMaw4bubaSBk4bqhbmc6DQoNCk91dGNvbWUgfiBUcmVhdG1lbnQgKyAoMXxJZCkgKyAoMXxJZDpTaWRlICkNCg0KKipDcm9zc2VkIGVmZmVjdDoqKg0KDQrEkMOieSBsw6AgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4gY+G7p2EgY8OhYyB54bq/dSB04buRIGdpYW8gbmhhdSwgaGF5IGtow7RuZyBjw7MgY8ahIHPhu58gxJHhu4MgbOG6rXAgbHXhuq1uIHLhurFuZyBjaMO6bmcgbOG7k25nIGdow6lwIHbhu5tpIG5oYXUuIFRow60gZOG7pSDEkeG7gyBraOG6o28gc8OhdCBt4bupYyDEkeG7mSB0xrDGoW5nIGjhu6NwIGdp4buvYSAzIGLDoWMgc8SpIGNo4bqpbiDEkW/DoW4gaMOsbmgg4bqjbmgsIG3hu5dpIG5nxrDhu51pIHRyb25nIHPhu5EgaOG7jSBz4bq9IGzhuqduIGzGsOG7o3QgxJHhu41jIDMgaMOsbmgg4bqjbmggQ3Qgc2NhbiBj4bunYSBjw7luZyBt4buZdCBi4buHbmggbmjDom4gxJHGsOG7o2MgY2jhu6VwIHRyb25nIDMgbOG6p24ga2jDoWMgbmhhdSBi4bqxbmcgMyB0aGnhur90IGLhu4sga2jDoWMgbmhhdS4gTmjGsCB24bqteSB54bq/dSB04buRIG5n4bqrdSBuaGnDqm4gc+G6vSBsw6AgY3Jvc3NlZCBlZmZlY3QgPSAoMXxEb2N0b3IpICsgKDF8UmVwbGljYXRpb24pIGNo4bupIGtow7RuZyBwaOG6o2kgbMOgIG5lc3RlZCBlZmZlY3QgID0gKDF8RG9jdG9yOlJlcGxpY2F0aW9uKSB2w6wgbeG7l2kgbGV2ZWwgY+G7p2EgcmVwbGljYXRpb24gbMOgIG5oxrAgbmhhdSBjaG8gY+G6oyAzIGxldmVscyBj4bunYSBEb2N0b3IuDQoNCioqTXVsdGlsZXZlbCBtb2RlbCBoYXkgSGllcmFyY2hpY2FsIG1vZGVsOioqDQoNCkPDoWMgdGh14bqtdCBuZ+G7ryBuw6B5IHBo4buVIGJp4bq/biB0cm9uZyBraG9hIGjhu41jIHjDoyBo4buZaSB2w6Aga2luaCB04bq/IGjGoW4gbMOgIFkgaOG7jWMsIHRow60gZOG7pSB0aMaw4budbmcgZ+G6t3AgbmjhuqV0IHRyb25nIHPDoWNoIHbhu58gbMOgIG5naGnDqm4gY+G7qXUgduG7gSBjw6FjIGNo4buJIHPhu5EgZ2nDoW8gZOG7pWMgduG7m2kgxJHGoW4gduG7iyBsw6AgaOG7jWMgc2luaCB24bubaSB54bq/dSB04buRIG7huqt1IG5oacOqbiDEkcaw4bujYyBwaMOibiBj4bqlcCB0aGVvIHRow6BuaCBwaOG7kSwgdHLGsOG7nW5nLCBs4bubcCDigKYgdHV5IG5oacOqbiBraMOhaSBuaeG7h20gduG7gSBj4bqldSB0csO6YyDEkWEgY+G6pXAgdHJvbmcgbcO0IGjDrG5oIGPFqW5nIGPDsyB0aOG7gyBn4bq3cCB0cm9uZyBuZ2hpw6puIGPhu6l1IFkga2hvYSB2w6AgZOG7i2NoIHThu4UgaOG7jWMuIFRow60gZOG7pSBj4buZbmcgxJHhu5NuZyBkw6JuIGPGsCBjw7MgdGjhu4MgcGjDom4gY+G6pXAgdGjDoG5oIFRow6BuaCBwaOG7kSwgUXXhuq1uLCBwaMaw4budbmcsIHThu5UsIHYuLnYgLHJhbmRvbSBlZmZlY3QgY8OzIGThuqFuZyBj4bqldSB0csO6YyDEkWEgY+G6pXAgY8WpbmcgdMawxqFuZyB04buxIG5oxrAgaGnhu4d1IOG7qW5nIGzhu5NuZyBnaMOpcCAobmVzdGVkKSB2w6AgY8OzIHRo4buDIMSRxrDhu6NjIHZp4bq/dCBuaMawOg0KDQrEkGnhu4NtIHRoaSDEkeG6oWkgaOG7jWMgfiBHaeG7m2kgdMOtbmggKyAoMXxUaMOgbmggcGjhu5EpICsgKDF8VGjDoG5oIHBo4buROlRyxrDhu51uZykgKyAoMXxUaMOgbmggcGjhu5E6VHLGsOG7nW5nOkzhu5twKQ0KDQoqKlJlcGVhdGVkIG1lYXN1cmUgdsOgIExvbmdpdHVkaW5hbCBzdHVkeSoqDQoNCsSQw6J5IGzDoCB0aGnhur90IGvhur8gbmdoacOqbiBj4bupdSBy4bqldCBwaOG7lSBiaeG6v24sIGtoaSBt4buZdCBr4bq/dCBxdeG6oyAob3V0Y29tZSkgxJHGsOG7o2Mga2jhuqNvIHPDoXQgbmhp4buBdSBs4bqnbiAodOG6oWkgbmhp4buBdSB0aOG7nWkgxJFp4buDbSBraMOhYyBuaGF1KSB0csOqbiBjw7luZyBt4buZdCDEkeG7kWkgdMaw4bujbmcuIFRow60gZOG7pTogTeG7mXQgdGjhu60gbmdoaeG7h20gdGh14buRYyB0cm9uZyDEkcOzIDEgYmlvbWFya2VyIGhheSDEkeG6oWkgbMaw4bujbmcgbMOibSBzw6BuZyDEkcaw4bujYyBraOG6o28gc8OhdCB04bqhaSB0aOG7nWkgxJFp4buDbSB0MCwgc2F1IMSRw7MgYuG7h25oIG5ow6JuIMSRxrDhu6NjIHBow6JuIG5ow7NtIMSRaeG7gXUgdHLhu4sgKHBsYWNlYm8vdGh14buRYyBYKSB2w6AgdGEgbOG6t3AgbOG6oWkgcGjDqXAgxJFvIG3hu5dpIDMgdGjDoW5nIHRyb25nIHbDsm5nIDEgbsSDbSwgbmjGsCB24bqteSB0YSBjw7MgNSB0aOG7nWkgxJFp4buDbSBCYXNlbGluZSwzLDYsOSwxMi4gTcO0IGjDrG5oIHbhu5tpIHJhbmRvbSBzbG9wZSBkbyDEkcOzIHPhur0gxJHGsOG7o2Mgdmnhur90IGzDoDoNCk91dGNvbWUgfiBCYXNlbGluZSpUcmVhdG1lbnQgKyBUaW1lKyAoVHJlYXRtZW50IHxQYXRpZW50IElkKQ0KDQojIFRow60gZOG7pSBtaW5oIGjhu41hOiBEYXRhc2V0IEVwaWxlcHRpYw0KDQpFcGlsZXB0aWMgbMOgIDEgZGF0YXNldCBraW5oIMSRaeG7g24gY2hvIG3DtCBow6xuaCByYW5kb20gZWZmZWN0LCBuw7MgxJHDoyDEkcaw4bujYyBz4butIGThu6VuZyBi4bufIG5oaeG7gXUgdMOhYyBnaeG6oyBuaMawIFRoYWxsICxWYWlsIFsxOTkwXSwgQnJlc2xvdyB2w6AgQ2xheXRvblsxOTkzXSwgTGVlIHbDoCBOZWxkZXIgWzE5OTYsIDIwMDBdLiDEkMOieSBsw6AgbeG7mXQgbmdoacOqbiBj4bupdSB0aOG7rSBuZ2hp4buHbSBsw6JtIHPDoG5nIHRyb25nIMSRw7MgY8OzIDIgbmjDs20gYuG7h25oIG5ow6JuIChuPTU5KSBt4bqvYyBi4buHbmggxJHhu5luZyBraW5oLiBU4bqnbiBzdeG6pXQgY8ahbiDEkeG7mW5nIGtpbmggxJHGsOG7o2MgxJHhur9tIHRyb25nIG3hu5dpIDIgdHXhuqduIHbDoCBs4bq3cCBs4bqhaSA1IGzhuqduLiBTYXUgMiB0deG6p24gxJHhuqd1IHRpw6puLCB2aeG7h2MgxJFp4buBdSB0cuG7iyBi4bqvdCDEkeG6p3UgduG7m2kgUGxhY2VibyB2w6AgbG/huqFpIHRodeG7kWMgxJFp4buBdSB0cuG7iyBj4bqnbiBraOG6o28gc8OhdC4gU2F1IMSRw7Mgc+G7kSBjxqFuIMSR4buZbmcga2luaCDEkcaw4bujYyBnaGkgbmjhuq1uIHNhdSBt4buXaSAyIHR14bqnbiB0cm9uZyA0IGzhuqduIHTDoWkga2jDoW0uDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmxpYnJhcnkoZ2FtbHNzKQ0KDQpteV90aGVtZSA8LSBmdW5jdGlvbihiYXNlX3NpemUgPTgsIGJhc2VfZmFtaWx5ID0gInNhbnMiKXsNCiAgdGhlbWVfYncoYmFzZV9zaXplID0gYmFzZV9zaXplLCBiYXNlX2ZhbWlseSA9IGJhc2VfZmFtaWx5KSArDQogICAgdGhlbWUoDQogICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXkiKSwNCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSksDQogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiIzUxMDE2NiIsIGNvbG9yID0gTkEsIHNpemUgPTAuNSksDQogICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSA4LCBjb2xvciA9ICJ3aGl0ZSIpLA0KICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKDAuNSwwLjUsMC41LDAuNSkNCiAgICApDQp9DQoNCmBgYA0KDQojIFRoxINtIGTDsiBz4buRIGxp4buHdQ0KDQpOaMawIHRoxrDhu51uZyBs4buHLCBOaGkgc+G6vSBi4bqvdCDEkeG6p3UgdGjEg20gZMOyIHPhu5EgbGnhu4d1IGLhurFuZyBiaeG7g3UgxJHhu5MNCg0KR2hpIGNow7o6IGvhur90IHF14bqjIOG7nyB0aOG7nWkgxJFp4buDbSBiYW4gxJHhuqd1IHRyxrDhu5tjIMSRaeG7gXUgdHLhu4sgxJHGsOG7o2MgY2h1eeG7g24gc2FuZyB0aGFuZyBsb2dhcml0aG0gKGxiYXNlKQ0KVHXhu5VpIGPhu6dhIGLhu4duaCBuaMOibiBjxaluZyDEkcaw4bujYyBjaHV54buDbiBzYW5nIHRoYW5nIGxvZ2FyaXRobQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCg0KZXBpbD1yZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHViLmNvbS92aW5jZW50YXJlbGJ1bmRvY2svUmRhdGFzZXRzL21hc3Rlci9jc3YvTUFTUy9lcGlsLmNzdiIpDQoNCmVwaWwkYmFzZT1hcy5mYWN0b3IoZXBpbCRiYXNlKQ0KDQplcGlsJT4lZ2dwbG90KGFlcyh4PXksZmlsbD10cnQpKSsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPS44KSsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4sZmlsbD10cnQpLGNvbG91cj0iYmxhY2siLGJpbndpZHRoID0gMyxhbHBoYT0wLjcsc2hvdy5sZWdlbmQgPSBGKSsNCiAgbXlfdGhlbWUoKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2ZmOWMwNyIsIiNkNjBhMjkiKSkrDQogIGZhY2V0X3dyYXAofnBlcmlvZCxuY29sPTIpKw0KICBnZ3RpdGxlKCJEZW5zaXR5ICsgSGlzdG9ncmFtIikNCg0KZXBpbCU+JWdncGxvdChhZXMoeD10cnQseT15LGdyb3VwPWZhY3RvcihsYmFzZSkpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPWxiYXNlKSxzaXplPTEuMixhbHBoYT0wLjIsc2U9RixtZXRob2Q9ImxtIikrDQogIG15X3RoZW1lKCkrDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9IiNmZmE1MjgiLGhpZ2g9IiNkYjExNTQiKSsNCiAgZmFjZXRfd3JhcCh+cGVyaW9kLHNjYWxlcz0iZnJlZSIpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9dHJ0LHk9eSxncm91cD1mYWN0b3IobGFnZSkpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPWxhZ2UpLHNpemU9MSxhbHBoYT0wLjUsc2U9RixtZXRob2Q9ImxtIikrDQogIG15X3RoZW1lKCkrDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9IiNmZmE1MjgiLGhpZ2g9IiNkYjExNTQiKSsNCiAgZmFjZXRfd3JhcCh+cGVyaW9kLHNjYWxlcz0iZnJlZSIpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9cGVyaW9kLHk9eSxncm91cD0xKSkrDQogIGdlb21fcGF0aChhZXMoY29sb3I9dHJ0KSxhbHBoYT0wLjgsc2l6ZT0xKSsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9dHJ0LHNoYXBlPXRydCksYWxwaGE9MC41LHNpemU9MikrDQogIG15X3RoZW1lKDUpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNmZjljMDciLCIjZDYwYTI5IikpKw0KICBmYWNldF93cmFwKH5zdWJqZWN0LHNjYWxlcz0iZnJlZSIsbmNvbD03KQ0KDQplcGlsJT4lZ2dwbG90KGFlcyh4PXBlcmlvZCx5PXksZ3JvdXA9MSkpKw0KICBnZW9tX3BhdGgoYWVzKGNvbG9yPXRydCksYWxwaGE9MC40LHNpemU9MSkrDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yPXRydCxzaGFwZT10cnQpLGFscGhhPTAuNSxzaXplPTIpKw0KICBteV90aGVtZSg1KSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjZmY5YzA3IiwiI2Q2MGEyOSIpKQ0KDQplcGlsJT4lZ2dwbG90KGFlcyh4PXBlcmlvZCx5PXksZ3JvdXA9dHJ0LGNvbG9yPXRydCkpKw0KICBnZW9tX3Ntb290aChhbHBoYT0wLjUsc2U9RixtZXRob2Q9ImdsbSIpKw0KICBteV90aGVtZSgpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNmZjljMDciLCIjZDYwYTI5IikpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9cGVyaW9kLHk9bG9nKHkpLGdyb3VwPWZhY3RvcihsYmFzZSksY29sb3I9bGJhc2UpKSsNCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MC41LHNlPUYsbWV0aG9kPSJnbG0iKSsNCiAgbXlfdGhlbWUoKSsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3c9IiNmNzYyMGMiLG1pZD0iI2Q2MDY1MiIsaGlnaD0iIzYyMDE3YSIpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9cGVyaW9kLHk9bG9nKHkpLGdyb3VwPWxhZ2UsY29sb3I9bGFnZSkpKw0KICBnZW9tX3Ntb290aChhbHBoYT0wLjUsc2U9RixtZXRob2Q9ImdsbSIpKw0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGhpZ2g9IiNhYTAzYmEiLGxvdz0iI2ZjNmUwMiIsbWlkPSIjZDYwNjUyIikrDQogIG15X3RoZW1lKCkNCg0KZXBpbCU+JWdncGxvdChhZXMoeD1wZXJpb2QseT1sb2coeSksZ3JvdXA9bGJhc2UsY29sb3I9bGJhc2UpKSsNCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MC41LHNlPUYsbWV0aG9kPSJnbG0iKSsNCiAgbXlfdGhlbWUoKStmYWNldF93cmFwKH50cnQsbmNvbD0yKSsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MihoaWdoPSIjYWEwM2JhIixsb3c9IiNmYzZlMDIiLG1pZD0iI2Q2MDY1MiIpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9bGFnZSx5PWxvZyh5KSxncm91cD10cnQpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPXRydCxmaWxsPXRydCksYWxwaGE9MC4zLG1ldGhvZD0ibG0iKSsNCiAgdGhlbWVfYncoKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjZmY5YzA3IiwiI2Q2MGEyOSIpKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmN2I4MGMiLCIjZDYwYTI5IikpDQoNCmVwaWwlPiVnZ3Bsb3QoYWVzKHg9bGFnZSx5PWxvZyh5KSxncm91cD10cnQsY29sb3I9dHJ0KSkrDQogIGdlb21fcG9pbnQoYWxwaGE9MC41KSsNCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MC41LHNlPUYsbWV0aG9kPSJnbG0iKSsNCiAgbXlfdGhlbWUoKStmYWNldF93cmFwKH5wZXJpb2Qsc2NhbGVzPSJmcmVlIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI2ZmOWMwNyIsIiNkNjBhMjkiKSkNCg0KZXBpbCU+JWdncGxvdChhZXMoeD1sYmFzZSx5PWxvZyh5KSxncm91cD10cnQsY29sb3I9dHJ0KSkrDQogIGdlb21fcG9pbnQoYWxwaGE9MC41KSsNCiAgZ2VvbV9zbW9vdGgoYWxwaGE9MC41LHNlPUYsbWV0aG9kPSJnbG0iKSsNCiAgbXlfdGhlbWUoKStmYWNldF93cmFwKH5wZXJpb2Qsc2NhbGVzPSJmcmVlIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI2ZmOWMwNyIsIiNkNjBhMjkiKSkNCg0KYGBgDQoNCkPDoWMgYmnhu4N1IMSR4buTIGfhu6NpIMO9IHLhurFuZyA6DQoNCkJp4bq/biBr4bq/dCBxdeG6oyBsw6AgbeG7mXQgc+G7kSDEkeG6v20sIHbhu5tpIGThuqV1IGhp4buHdSBvdmVyZGlzcGVyc2lvbiBuaOG6uSwgbsOqbiBjw7MgdGjhu4MgaOG7jSBwaMOibiBwaOG7kWkgcGjDuSBo4bujcCBjaG8gbsOzIHPhur0gbMOgIG5lZ2F0aXZlIGJpbm9taWFsIA0KDQpHacOhIHRy4buLIGJhc2VsaW5lIGPDsyB0w6FjIMSR4buZbmcgbMOqbiBnacOhIHRy4buLIGJp4bq/biBr4bq/dCBxdeG6oyB0cm9uZyBxdcOhIHRyw6xuaCDEkWnhu4F1IHRy4buLIHThuqFpIGPhuqMgNCB0aOG7nWkgxJFp4buDbSwgbmjGsG5nIGNoxrBhIHLDtSBsaeG7h3UgY8OzIHTGsMahbmcgdMOhYyBnaeG7r2EgbsOzIHbDoCBoaeG7h3Ug4bupbmcgxJFp4buBdSB0cuG7iyBoYXkga2jDtG5nDQoNCk5ow6xuIGNodW5nLCBuaMOzbSDEkWnhu4F1IHRy4buLIGdp4bqjbSByw7UgbsOpdCB04bqnbiBzdeG6pXQgY8ahbiDEkeG7mW5nIGtpbmggc28gduG7m2kgbmjDs20gUGxhY2VibywgbmjGsG5nIGhp4buHdSDhu6luZyBuw6B5IGtow7RuZyBo4bqxbmcgxJHhu4tuaCDhu58gbeG7jWkgYuG7h25oIG5ow6JuDQoNClR14buVaSBi4buHbmggbmjDom4ga2jDtG5nIGPDsyDhuqNuaCBoxrDhu59uZyDEkcOhbmcga+G7gyDEkeG6v24ga+G6v3QgcXXhuqMgxJFp4buBdSB0cuG7iw0KDQpOaGkgY2h1eeG7g24gZOG6oW5nIGJp4bq/biBz4buRIHBlcmlvZCB0aMOgbmggbeG7mXQgY29udHJhc3QgdmFyaWFibGUgduG7m2kgNCBj4bqlcCBi4bqtYyBnacOhIHRy4buLOiANCmBgYHtyfQ0KZXBpbCR2aXNpdD0oMiplcGlsJHBlcmlvZC01KS8xMA0KDQpmYWN0b3IoZXBpbCR2aXNpdCklPiVsZXZlbHMoLikNCg0KYGBgDQoNCiMgSMOgbSBnYW1sc3NOUCgpDQoNCkjDoG0gZ2FtbHNzTlAoKSBjw7MgY8O0bmcgZOG7pW5nIGzDoCBtw7QgaMOsbmggaMOzYSBtYXJnaW5hbCBsaWtlbGlob29kLCBi4bqjbiBjaOG6pXQgY+G7p2EgbsOzIGzDoCDGsOG7m2MgdMOtbmggdOG7lW5nIHF1w6F0IChoaeG7h3Ug4bupbmcgbmfhuqt1IG5oacOqbiDEkcaw4bujYyAgcGjDom4gdMOtY2ggdHJvbmcgYuG6o24gdGjDom4gYWxnb3JpdGhtIGPhu6dhIEdhbWxzcykuIA0KDQpUw7l5IGzhu7FhIGNo4buNbiBj4bunYSBuZ8aw4budaSBkw7luZywgaMOgbSBnYW1sc3NOUCBjaG8gcGjDqXAgw6FwIGThu6VuZyAyIHBoxrDGoW5nIHBow6FwOg0KDQoxKSBQaMOpcCBj4bqndSBwaMawxqFuZyBHYXVzc2lhbiAoUXVhZHJhdHVyZSkgbmjhurFtIMaw4bubYyBsxrDhu6NuZyBtYXJnaW5hbCBsaWtlbGloaG9kIGLhurFuZyBjw6FjaCB0aGF5IHRo4bq/IHTDrWNoIHBow6JuIHRyb25nIGtob+G6o25nIHBow6JuIHBo4buRaSBjaHXhuqluIGPhu6dhIHJhbmRvbSBlZmZlY3QgYuG6sW5nIHBow6lwIGPhu5luZyBkaeG7h24gdMOtY2ggY+G7p2Egbmhp4buBdSB04bupIGdpw6FjIChjb24gc+G7kSBkbyBuZ8aw4budaSBkw7luZyBxdXkgxrDhu5tjKS4gDQoNCkPDtG5nIGThu6VuZyBj4bunYSBwaMawxqFuZyBwaMOhcCBuw6B5IGzDoCBk4buxbmcgbcO0IGjDrG5oIHbhu5tpIFJhbmRvbSBpbnRlcmNlcHQgKDEgeeG6v3UgdOG7kSBuZ+G6q3Ugbmhpw6puIGxpw6puIHThu6VjIHbDoCBjw7MgcGjDom4gcGjhu5FpIGNodeG6qW4pIGNobyB0aGFtIHPhu5EgduG7iyB0csOtIHRydW5nIHTDom0gTXUuDQoNCjIpIHBoxrDGoW5nIHBow6FwIHBoaSB0aGFtIHPhu5EgKG5vbiBwYXJhbWV0cmljKSBjaG8geeG6v3UgdOG7kSBuZ+G6q3Ugbmhpw6puIHLhu51pIHLhuqFjIDsgduG7m2kgY8O0bmcgZOG7pW5nIGzDoCBtw7QgaMOsbmggaMOzYSByYW5kb20gaW50ZXJjZXB0IGhv4bq3YyBj4bqjIHJhbmRvbSBpbnRlcmNlcHQgdsOgIHJhbmRvbSBzbG9wZSBjaG8gdOG7qyAxIMSR4bq/biA0IHRoYW0gc+G7kSBj4bunYSAxIGjhu40gcGjDom4gcGjhu5FpIChNdSwgU2lnbWEsIE51IHbDoCBUYXUpLiANCg0KKipDw7ogcGjDoXAgaMOgbSBnYW1sc3NOUCoqDQoNCkjDoG0gZ2FtbHNzTlAgxJHGsOG7o2MgaOG7lyB0cuG7oyBi4bufaSBtb2R1bGUgZ2FtbHNzLm14IGPDsyBjw7RuZyBk4bulbmc6DQoNCk3DtCBow6xuaCBow7NhIHJhbmRvbSBpbnRlcmNlcHQgbGnDqm4gdOG7pWMsIHBow6JuIHBo4buRaSBjaHXhuqluLCBjaG8gTXUNCkhheTogROG7sW5nIG3DtCBow6xuaCBwaGkgdGhhbSBz4buRIHbhu5tpIHJhbmRvbSBpbnRlcmNlcHQgdsOgIHJhbmRvbSBzbG9wZSBjaG8gY+G6oyBNdSwgU2lnbWEsIE51IHbDoCBUYXUgY+G7p2EgMSBo4buNIHBow6JuIHBo4buRaSB0w7l5IGNo4buNbi4NCg0KVMO5eSBjaOG7iW5oIHJhbmRvbSA9IGPDsyBu4buZaSBkdW5nIGzDoCAxIGPDtG5nIHRo4bupYyDEkeG7gyBiaeG7g3UgdGjhu4sgY2hvIHJhbmRvbSBlZmZlY3QsIHRow60gZOG7pSByYW5kb20gaW50ZXJjZXB0IOG7nyBj4bqlcCDEkeG7mSBwaMOibiBuaMOzbTogcmFuZG9tID0gfiAxfEYNCg0KUmFuZG9tIHNsb3BlIOG7nyBj4bqlcCDEkeG7mSBwaMOibiBuaMOzbTogcmFuZG9tID0gfiBYfEYNCg0KTuG6v3UgcmFuZG9tIGVmZmVjdCDhu58gY+G6pXAgxJHhu5kgcXVhbiBzw6F0OiBraMO0bmcgY+G6p24ga2hhaSBiw6FvIGNobyB0w7l5IGNo4buJbmggcmFuZG9tDQpUw7l5IGNo4buJbmggTWl4dHVyZSA6IHBoxrDGoW4gcGjDoXAgxrDhu5tjIGzGsOG7o25nOg0KDQpQaMOpcCBj4bqndSBwaMawxqFuZyBHYXVzc2lhbjogZ3EsIGTDuW5nIGNobyByYW5kb20gaW50ZXJjZXB0IHBow6JuIHBo4buRaSBjaHXhuqluDQpOcD0gbm9ucGFyYW1ldHJpYyByYW5kb20gZWZmZWN0IG1vZGVscw0KSz0gc+G7kSDEkWnhu4NtIHRyb25nIHBow6lwIMaw4bubYyBsxrDhu6NuZyBucCBoYXkgZ3ANCktoaSBtdeG7kW4gbeG7nyBy4buZbmcgcmFuZG9tIGVmZmVjdCBjaG8gY8OhYyB0aGFtIHPhu5Ega2jDoWMgbmjGsCBTaWdtYSwgTnUgdsOgIFRhdSwgdGEgcGjhuqNpIGtoYWkgYsOhbyBjw7RuZyB0aOG7qWMgY2hvIGPDoWMgdGjDoG5oIHThu5EgbsOgeSwgcGjhuqduICByYW5kb20gZWZmZWN0IMSRxrDhu6NjIGJp4buDdSB0aOG7iyBi4bqxbmcgdMOqbiAxIG1hdHJpeCBn4buNaSBsw6AgTUFTUywgdGjDrSBk4bulOiBzaWdtYS5mbyB+IFgrIE1BU1MgY2hvIGJp4bq/dCBNQVNTIG7hurFtIOG7nyB24buLIHRyw60gSW50ZXJjZXB0LCBjw7JuIHNpZ21hLmZvIH4gWCogTUFTUyBjaG8gYmnhur90IE1BU1MgbuG6sW0gY+G6oyDhu58gaW50ZXJjZXB0IHbDoCBzbG9wZS4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdhbWxzcy5teCkNCg0KZXBpbCRzdWJqZWN0PWFzLmZhY3RvcihlcGlsJHN1YmplY3QpDQoNCg0KbTA8LWdhbWxzc05QKHl+MSwgDQogICAgICAgICAgICAgICByYW5kb209fjF8c3ViamVjdCwNCiAgICAgICAgICAgICAgIEs9MTUsDQogICAgICAgICAgICAgICBtaXh0dXJlPSJncSIsDQogICAgICAgICAgICAgICBkYXRhPWVwaWwsIA0KICAgICAgICAgICAgICAgZmFtaWx5PU5CSSkNCg0KbTAwPC1nYW1sc3NOUCh5fnRydCwgDQogICAgICAgICAgICAgIHJhbmRvbT1+MXxzdWJqZWN0LA0KICAgICAgICAgICAgICBLPTE1LA0KICAgICAgICAgICAgICBtaXh0dXJlPSJncSIsDQogICAgICAgICAgICAgIGRhdGE9ZXBpbCwgDQogICAgICAgICAgICAgIGZhbWlseT1OQkkpDQoNCm0xMDwtZ2FtbHNzTlAoeX50cnQrdmlzaXQsIA0KICAgICAgICAgICAgIHJhbmRvbT1+MXxzdWJqZWN0LA0KICAgICAgICAgICAgIEs9MTUsDQogICAgICAgICAgICAgbWl4dHVyZT0iZ3EiLA0KICAgICAgICAgICAgIGRhdGE9ZXBpbCwgDQogICAgICAgICAgICAgZmFtaWx5PU5CSSkNCg0KbTExPC1nYW1sc3NOUCh5fnRydCtsYmFzZSt2aXNpdCwgDQogICAgICAgICAgICAgcmFuZG9tPX4xfHN1YmplY3QsDQogICAgICAgICAgICAgSz0xNSwNCiAgICAgICAgICAgICBtaXh0dXJlPSJncSIsDQogICAgICAgICAgICAgZGF0YT1lcGlsLCANCiAgICAgICAgICAgICBmYW1pbHk9TkJJKQ0KDQptMTI8LWdhbWxzc05QKHl+dHJ0KmxiYXNlK3Zpc2l0LCANCiAgICAgICAgICAgICByYW5kb209fjF8c3ViamVjdCwNCiAgICAgICAgICAgICBLPTE1LA0KICAgICAgICAgICAgIG1peHR1cmU9ImdxIiwNCiAgICAgICAgICAgICBkYXRhPWVwaWwsIA0KICAgICAgICAgICAgIGZhbWlseT1OQkkpDQoNCm0xMzwtZ2FtbHNzTlAoeX50cnQqbGJhc2UrdmlzaXQrbGFnZSwgDQogICAgICAgICAgICAgIHJhbmRvbT1+MXxzdWJqZWN0LA0KICAgICAgICAgICAgICBLPTE1LA0KICAgICAgICAgICAgICBtaXh0dXJlPSJncSIsDQogICAgICAgICAgICAgIGRhdGE9ZXBpbCwgDQogICAgICAgICAgICAgIGZhbWlseT1OQkkpDQoNCkdBSUMobTAsbTAwLG0xMCxtMTEsbTEyLG0xMyxrPWxvZyhucm93KGVwaWwpKSkNCg0KTFIudGVzdChtMTEsbTEyKQ0KDQpMUi50ZXN0KG0xMCxtMTEpDQoNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkobTExKQ0KDQpgYGANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQojbWV0aG9kIG5wDQoNCm0yMDwtZ2FtbHNzTlAoeX50cnQrbGJhc2UrdmlzaXQsIA0KICAgICAgICAgICAgICByYW5kb209fjF8c3ViamVjdCwNCiAgICAgICAgICAgICAgSz0xNSwNCiAgICAgICAgICAgICAgbWl4dHVyZT0ibnAiLA0KICAgICAgICAgICAgICBkYXRhPWVwaWwsIA0KICAgICAgICAgICAgICBmYW1pbHk9TkJJKQ0KDQoNCm0yMTwtZ2FtbHNzTlAoeX50cnQqbGJhc2UrdmlzaXQsIA0KICAgICAgICAgICAgICByYW5kb209fjF8c3ViamVjdCwNCiAgICAgICAgICAgICAgSz0xNSwNCiAgICAgICAgICAgICAgbWl4dHVyZT0ibnAiLA0KICAgICAgICAgICAgICBkYXRhPWVwaWwsIA0KICAgICAgICAgICAgICBmYW1pbHk9TkJJKQ0KDQptMjI8LWdhbWxzc05QKHl+dHJ0KmxiYXNlK3Zpc2l0LCANCiAgICAgICAgICAgICByYW5kb209fnRydHxzdWJqZWN0LA0KICAgICAgICAgICAgIEs9MjAsDQogICAgICAgICAgICAgbWl4dHVyZT0ibnAiLA0KICAgICAgICAgICAgIGRhdGE9ZXBpbCwgDQogICAgICAgICAgICAgZmFtaWx5PU5CSSkNCg0KR0FJQyhtMjAsbTIxLG0yMixrPWxvZyhucm93KGVwaWwpKSkNCg0KcGxvdE1QKG0yMiRtYXNzLnBvaW50c1ssMV0sIG0yMiRtYXNzLnBvaW50c1ssMl0sIG0yMiRwcm9iLHRoZXRhPTUwLA0KICAgICAgIHBoaT0zMCxjb2wgPSAiZ29sZCIpDQoNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkobTIwKQ0KYGBgDQoNCiMgSMOgbSByYW5kb20oKSB2w6AgcmUoKQ0KDQpLaMOhYyB24bubaSBow6BtIGdhbWxzc05QKCksIGjDoG0gcmFuZG9tKCkgdsOgIHJlKCApIHPhu60gZOG7pW5nIHBow6lwIMaw4bubYyBsxrDhu6NuZyBsaWtlbGlob29kIGPhu6VjIGLhu5kgKGxvY2FsKSBjw7JuIGfhu41pIGzDoCBwaMawxqFuZyBwaMOhcCBwZW5hbGl6ZWQgcXVhc2kgbGlrZWxpaG9vZCAoUFFMKSDEkeG7gyDGsOG7m2MgdMOtbmggam9pbnQgbGlrZWxpaG9vZCwgbMO6YyBuw6B5IHPhu7EgYmnhur9uIHRoacOqbiBiaeG6v24gc+G7kSBr4bq/dCBxdeG6oyBz4bq9IMSRxrDhu6NjIG3DtCBow6xuaCBow7NhIHRow7RuZyBxdWEgeMOhYyBzdeG6pXQgY8OzIMSRaeG7gXUga2nhu4duIHTDuXkgdGh14buZYyB2w6BvIHJhbmRvbSBlZmZlY3QuDQoNCkjDoG0gcmFuZG9tKCkgdHJvbmcgZ2FtbHNzIGzDoCBwaGnDqm4gYuG6o24gY+G7p2EgaMOgbSBjw7luZyB0w6puIHRyb25nIHBhY2thZ2UgZ2FtIGPhu6dhIFRpc2hpYnJhbmkgxJHGsOG7o2MgY+G6o2kgYmnDqm4gYuG7n2kgUmlnYnkgdsOgIFN0YXNpbm9wb3Vsb3MgxJHhu4MgZMO5bmcgY2hvIHBhY2thZ2UgZ2FtbHNzLiBOw7MgY2hvIHBow6lwIG3DtCBow6xuaCBow7NhIHJhbmRvbSBpbnRlcmNlcHQgY8OzIHBow6JuIHBo4buRaSBjaHXhuqluLg0KDQpIw6BtIHJlKCkgbMOgIG3hu5l0IGPDoW5oIGPhu61hIGxpw6puIHRow7RuZyB24bubaSBow6BtIGxtZSgpIHRyb25nIHBhY2thZ2UgbmxtZSwgbeG7mXQgY8O0bmcgY+G7pSBu4buVaSB0aeG6v25nIGNobyBtaXhlZCBtb2RlbCAoUGluaGVpcm8gdsOgIEJhdGVzLCAyMDAwKS4gTsOzIHPhur0gZ+G7jWkgcGFja2FnZSBubG1lKCkgdsOgIGvhur90IGjhu6NwIHBhY2thZ2UgbsOgeSB2w6AgZ2FtbHNzIMSR4buDIGThu7FuZyBtaXhlZCBtb2RlbCB24bubaSAzIGThuqFuZyA6IHJhbmRvbSBpbnRlcmNlcHQgdsOgIHJhbmRvbSBzbG9wZSwgbXVsdGlsZXZlbCBtb2RlbGxpbmcgdsOgIHJlcGVhdGVkIG1lYXN1cmVtZW50Lg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCiNSYW5kb20gZnVuY3Rpb24NCg0KbTMwPC1nYW1sc3MoeX50cnQrbGJhc2UrdmlzaXQrcmFuZG9tKHN1YmplY3QpLCANCiAgICAgICAgICAgZGF0YT1lcGlsLA0KICAgICAgICAgICBmYW1pbHk9TkJJLCANCiAgICAgICAgICAgdHJhY2U9RkFMU0UpDQoNCm0zMTwtZ2FtbHNzKHl+dHJ0KmxiYXNlK3Zpc2l0K3JhbmRvbShzdWJqZWN0KSwgDQogICAgICAgICAgICBzaWdtYS5mbz1+cmFuZG9tKHN1YmplY3QpLA0KICAgICAgICAgICAgZGF0YT1lcGlsLA0KICAgICAgICAgICAgZmFtaWx5PU5CSSwgDQogICAgICAgICAgICB0cmFjZT1GQUxTRSkNCg0KbTMyPC1nYW1sc3MoeX50cnQqbGJhc2UrdmlzaXQrbGFnZStyYW5kb20oc3ViamVjdCksIA0KICAgICAgICAgICAgc2lnbWEuZm89fnJhbmRvbShzdWJqZWN0KSwNCiAgICAgICAgICAgIGRhdGE9ZXBpbCwNCiAgICAgICAgICAgIGZhbWlseT1OQkksIA0KICAgICAgICAgICAgdHJhY2U9RkFMU0UpDQoNCkdBSUMobTMwLG0zMSxtMzIsaz1sb2cobnJvdyhlcGlsKSkpDQoNCnN1bW1hcnkobTMwKQ0KDQpyZGY9ZGF0YS5mcmFtZShtdT1hcy52ZWN0b3IobTMwJG11LnMpLA0KICAgICAgICAgICAgICAgbXVMPWFzLnZlY3RvcihtMzAkbXUucy0oc3FydChtMzAkbXUudmFyKS9zcXJ0KDIzNikpKSwNCiAgICAgICAgICAgICAgIG11VT1hcy52ZWN0b3IobTMwJG11LnMrKHNxcnQobTMwJG11LnZhcikvc3FydCgyMzYpKSksDQogICAgICAgICAgICAgICBJZD1lcGlsJHN1YmplY3QpDQoNCnJkZiU+JW11dGF0ZSguLFBvc2l0aXZlPWlmZWxzZShyZGYkbXU+MCwiUG9zaXRpdmUiLCJOZWdhdGl2ZSIpKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9YXMuZmFjdG9yKElkKSxjb2xvcj1Qb3NpdGl2ZSkpKw0KICAgIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bXVMLHltYXg9bXVVKSkrDQogIGdlb21fcG9pbnQoYWVzKHk9bXUpKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCxjb2xvcj0iYmx1ZSIsbGluZXR5cGU9MikrDQogIHRoZW1lX2J3KDUpKw0KICBjb29yZF9mbGlwKCkrDQogIHNjYWxlX3hfZGlzY3JldGUoIklkeCIpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNmZjljMDciLCIjZDYwYTI5IikpDQoNCmBgYA0KDQpIw6BtIHJlKCkgbWFuZyBs4bqhaSBjaG8gZ2FtbHNzIGto4bqjIG7Eg25nIHTGsMahbmcgxJHGsMahbmcgduG7m2kgcGFja2FnZSBubG1lKCksIHThu6ljIGzDoCBjw7MgdGjhu4MgbcO0IGjDrG5oIGjDs2Egbmjhu69uZyByYW5kb20gZWZmZWN0IHBo4bupYyB04bqhcCBtw6Aga2jDtG5nIGPhuqduIHjDqXQgxJHhur9uIGdp4bqjIMSR4buLbmggcGjDom4gcGjhu5FpIGNodeG6qW4uIEPDoWNoIHPhu60gZOG7pW5nIGjDoG0gcmUgY8WpbiB0xrDGoW5nIHThu7EgbmjGsCBwYWNrYWdlIG5sbWUsIG7hur91IGLhuqFuIMSRw6MgcXVlbiB0aHXhu5ljIHbhu5tpIG1peGVkIG1vZGVsIHRow6wgc+G6vSB0aOG6pXkgbsOzIHLhuqV0IGThu4UgZMOgbmcuDQpIw6BtIHJlIGPDsyB0aOG7gyDDoXAgZOG7pW5nIGNobyBi4bqldCBrw6wgdGhhbSBz4buRIHBow6JuIHBo4buRaSBuw6BvLCBuw7MgY8OzIG5o4buvbmcgdMO5eSBjaOG7iW5oIG5oxrAgc2F1Ow0KDQpmaXhlZCA9IGtoYWkgYsOhbyBu4buZaSBkdW5nIGNobyBjw7RuZyB0aOG7qWMgY+G7p2EgZml4ZWQgZWZmZWN0LCBuaMawbmcga2jDtG5nIGPhuqduIGtoYWkgYsOhbyBiaeG6v24ga+G6v3QgcXXhuqMNCg0KcmFuZG9tID0gY8O0bmcgdGjhu6ljIGNobyByYW5kb20gZWZmZWN0cw0KIH4geDEgKyDigKYgeG4gfGYxL+KApiAvIGZtDQogDQpW4bubaSBmMS/igKYvZm0gbMOgIG5lc3RlZCBzdHJ1Y3R1cmUgKGto4buRaSksIGtoaSByYW5kb20gZWZmZWN0IG7hurFtIOG7nyBj4bqlcCDEkeG7mSB04burbmcgcXVhbiBzw6F0LCBwaOG6p24gc2F1IGtow7RuZyBj4bqnbg0KfiB4MSArIOKApiB4biB8Zg0KDQpN4buZdCBkYW5oIHPDoWNoIGPDtG5nIHRo4bupYzoNCg0KTGlzdChmMT1wZERpYWcofiB4MSksIGYyPXBkU3ltbSh+IHgyKSkNCj0gcmFuZG9tIGludGVyY2VwdCB2w6Agc2xvcGUgY2hvIHgxIOG7nyBj4bqlcCDEkeG7mSBmYWN0b3IgZjEsIHbhu5tpIGNvdmFyaWFuY2Ugc3RydWN0dXJlIA0KcGREaWFnDQoNCnBkU3ltbT0gZ2nDoSB0cuG7iyBt4bq3YyDEkeG7i25oLCBj4bqldSB0csO6YyBtYXRyaXggeMOhYyDEkeG7i25oIGTGsMahbmcgdsOgIMSR4buRaSB44bupbmcNCg0KcGRCbG9ja2VkOiBj4bqldSB0csO6YyBk4bqhbmcga2jhu5FpIOKAkyBwaMOibiBnacOhYyANCg0KcmFuZG9tPWxpc3QoZjE9cGRCbG9ja2VkKGxpc3QocGRJbmRlbnQofjEpLHBkSWRlbnQofmYyLTEpKSkpDQoNCnBkQ29tcFN5bW06IGPhuqV1IHRyw7pjIMSR4buRaSB44bupbmcgdOG7lSBo4bujcCwgdGjDrWNoIGjhu6NwIGNobyBuZXN0ZWQgcmFuZG9tIGVmZmVjdCAodGjDrSBk4bulIGZhY3RvciBmMiBuZXN0ZWQgdHJvbmcgZmFjdG9yIGYxOg0KDQpyYW5kb209bGlzdChmMT1wZENvcG1TeW1tKH5mMi0xKSkNCg0KcGREaWFnOiBj4bqldSB0csO6YyBtYXRyaXggcGjDom4gZ2nDoWM6IGdp4bqjIMSR4buLbmggdMOtbmggxJHhu5ljIGzhuq1wIGdp4buvYSByYW5kb20gZWZmZWN0cw0KcmFuZG9tPXBkRGlhZyh+eDEreDIpDQoNCnRyb25nIMSRw7MgcmFuZG9tIHNsb3BlIGNobyB4MSB2w6AgeDIgdsOgIGludGVyY2VwdCBnaeG6oyDEkeG7i25oIG5oxrAgxJHhu5ljIGzhuq1wIHbhu5tpIG5oYXUNCnBkSWRlbnQ6IGPhuqV1IHRyw7pjIG1hdHJpeCBJZGVudGl0eQ0KDQpraGkgY+G6pXAgYuG6rWMgY+G7p2EgZmFjdG9yIGYxIGPDsyB2YXJpYW5jZSBuaMawIG5oYXU6DQpyYW5kb209cGRJZGVudCh+ZjEtMSkgDQoNCkNvcnJlbGF0aW9uOiB4w6FjIMSR4buLbmggY+G6pXUgdHLDumMgdMawxqFuZyBxdWFuIGLDqm4gdHJvbmcgbeG7l2kga2jhu5FpIChuaMOzbSkgaGF5IGVycm9yIHZhcmlhdGlvbiANCg0KxJDDonkgbMOgIG7GoWkgY2jDum5nIHRhIGPDsyB0aOG7gyDEkcawYSB2w6BvIG5o4buvbmcgY+G6pXUgdHLDumMgdMawxqFuZyBxdWFuIMSR4bq3YyBiaeG7h3QgY+G7p2EgdGltZSBzZXJpZXMgaGF5IHNwYXRpYWwgc3RydWN0dXJlcyANCg0KVuG7gSB0aW1lIHNlcmllcyB0YSBjw7M6DQoNCmNvckNvbXBTeW1tOiBj4bqldSB0csO6YyDEkeG7kWkgeOG7qW5nIHThu5UgaOG7o3AsIHTGsMahbmcg4bupbmcgduG7m2kgdMawxqFuZyBxdWFuIMSR4buTbmcgbmjhuqV0ICh1bmlmb3JtKQ0KDQpjb3JBUjE6IGF1dG9yZWdyZXNzaXZlIHByb2Nlc3MgY+G6pXAgMSwgaMOgbSBjb3JyZWxhdGlvbiA9IHJob15rIHbhu5tpIGs9MCwxLDLigKYNCg0KY29yQ0FSMTogY29udGludW91cyBhdXRvcmVncmVzc2l2ZSBwcm9jZXNzIEFSMSBwcm9jZXNzIGNobyAxIGhp4buHcCBiaeG6v24gc+G7kSB0aOG7nWkgZ2lhbiBsacOqbiB04bulYywgcmhvXnMgduG7m2kgcyA+ID0wDQoNCmNvckFSTUE9IGF1dG9yZWdyZXNzaXZlIG1vdmluZyBhdmVyYWdlIHByb2Nlc3MsIHbhu5tpIGFyYml0cmFyeSBvcmRlcnMgY2hvIHRow6BuaCBwaOG6p24gYXV0b3JlZ3Jlc3NpdmUgdsOgIG1vdmluZyBhdmVyYWdlIChBUk1BKHAscSkpIHTGsMahbmcg4bupbmcgduG7m2kgc+G7sSBr4bq/dCBo4bujcCBnaeG7r2EgcCBhdXRvcmVncmVzc2l2ZSBwYXJhbWV0ZXJzIHbDoCBxIG1vdmluZyBhdmVyYWdlIHBhcmFtZXRlcnMNCg0KY29yRXhwOiBleHBvbmVudGlhbCBzcGF0aWFsIGNvcnJlbGF0aW9uDQoNCmNvckdhdXM6IEdhdXNzaWFuIHNwYXRpYWwgY29ycmVsYXRpb24NCg0KY29yTGluOiBsaW5lYXIgc3BhdGlhbCBjb3JyZWxhdGlvbjsgDQoNCmNvclJhdGlvOiByYXRpb25hbCBxdWFkcmF0aWMgc3BhdGlhbCBjb3JyZWxhdGlvbg0KDQpjb3JTcGhlcjogc3BoZXJpY2FsIHNwYXRpYWwgY29ycmVsYXRpb24NCg0KbWV0aG9kID3igJ1NTOKAnSBoYXkg4oCcUkVNTOKAnQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCiNSRSBmdW5jdGlvbg0KDQptNDA8LWdhbWxzcyh5fnRydCtsYmFzZSt2aXNpdCsNCiAgICAgICAgICAgIHJlKHJhbmRvbT1+MXxzdWJqZWN0KSwNCiAgICAgICAgICAgIGZhbWlseT1OQkksDQogICAgICAgICAgICBkYXRhPWVwaWwsDQogICAgICAgICAgICB0cmFjZT1GQUxTRSkNCg0KbTQxPC1nYW1sc3MoeX50cnQqbGJhc2UrdmlzaXQrDQogICAgICAgICAgICAgIHJlKHJhbmRvbT1+MXxzdWJqZWN0KSwNCiAgICAgICAgICAgIGZhbWlseT1OQkksDQogICAgICAgICAgICBkYXRhPWVwaWwsDQogICAgICAgICAgICB0cmFjZT1GQUxTRSkNCg0KbTQyPC1nYW1sc3MoeX50cnQqbGJhc2UrdmlzaXQrDQogICAgICAgICAgICAgIHJlKHJhbmRvbT1+MXxzdWJqZWN0KSwNCiAgICAgICAgICAgIHNpZ21hLmZvPX5yZShyYW5kb209fjF8c3ViamVjdCksDQogICAgICAgICAgICBmYW1pbHk9TkJJLA0KICAgICAgICAgICAgZGF0YT1lcGlsLA0KICAgICAgICAgICAgdHJhY2U9RkFMU0UpDQoNCkdBSUMobTQwLG00MSxtNDIsaz1sb2cobnJvdyhlcGlsKSkpDQoNCnJkZj1kYXRhLmZyYW1lKG11PWFzLnZlY3RvcihtNDIkbXUucyksDQogICAgICAgICAgICAgICBzaWdtYT1hcy52ZWN0b3IobTQyJHNpZ21hLnMpLA0KICAgICAgICAgICAgICAgSWQ9ZXBpbCRzdWJqZWN0KQ0KDQpyZGYlPiVnYXRoZXIobXU6c2lnbWEsa2V5PSJQYXJhbWV0ZXIiLHZhbHVlPSJFZmZlY3QiKSU+JQ0KICBtdXRhdGUoLixQb3NpdGl2ZT1pZmVsc2UoRWZmZWN0PjAsIlBvc2l0aXZlIiwiTmVnYXRpdmUiKSklPiUNCiAgZ2dwbG90KGFlcyh4PWFzLmZhY3RvcihJZCkseT1FZmZlY3QsY29sb3I9UG9zaXRpdmUpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLGNvbG9yPSJibHVlIixsaW5ldHlwZT0yKSsNCiAgdGhlbWVfYncoNSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZSgiU3ViamVjdHMiKSsNCiAgZmFjZXRfd3JhcCh+UGFyYW1ldGVyLG5jb2w9MikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI2ZmOWMwNyIsIiNkNjBhMjkiKSkNCg0KDQpgYGANCg0KIyBNYXJnaW5hbCBlZmZlY3RzDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdGVybS5wbG90KG00MCwgcGFnZXM9MSkNCg0KdGVybS5wbG90KG0zMCwgcGFnZXM9MSkNCg0KZXBpbCU+JW11dGF0ZSguLHByZWRpY3Q9cHJlZGljdChtMzAsdHlwZT0icmVzcG9uc2UiKSklPiUNCiAgZ2dwbG90KGFlcyh4PXRydCx5PWxvZyhwcmVkaWN0KSkpKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHJ0KSxhbHBoYT0wLjcpKw0KICBteV90aGVtZSg1KStmYWNldF93cmFwKH5wZXJpb2QsbmNvbD0xKStjb29yZF9mbGlwKCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5YzA3IiwiI2Q2MGEyOSIpKQ0KDQpgYGANCg0KIyBL4bq/dCBsdeG6rW4NCg0KTmjhuq1uIHjDqXQ6DQpOaGkgduG7q2EgdHLDrG5oIGLDoHkgbeG7mXQgY8OhY2gga2jDoWkgcXXDoXQgY8O0bmcgZOG7pW5nIGPhu6dhIDQgaMOgbSB0cm9uZyBnYW1sc3MgY2hvIHBow6lwIG3DtCBow6xuaCBow7NhIHJhbmRvbSBlZmZlY3QgaGF5IHRow6BuaCBwaOG6p24gbmfhuqt1IG5oacOqbiwgYuG6pXQgxJHhu4tuaC4gQ8OzIHRo4buDIHRo4bqleSB0dXkgZ2FtbHNzIMSRw6MgaOG6pXAgdGh1IG7hu5lpIGR1bmcgdOG7qyBuaGnhu4F1IHBhY2thZ2UgY8OzIHRyxrDhu5tjIG7DsyBuaMawbmcgduG7gSBt4buNaSBwaMawxqFuZyBkaeG7h24sIHF1eSB0csOsbmggZOG7sW5nIG1peGVkIG1vZGVsIHRyb25nIGdhbWxzcyBraMO0bmcgdMawxqFuZyB0aMOtY2ggduG7m2kgbmjhu69uZyBjw7RuZyBj4bulIGtpbmggxJFp4buDbiBuaMawIG5sbWUgaGF5IGxtZTQuIFThu6ljIGzDoCB0YSBraMO0bmcgdGjhu4MgdHLDrWNoIHh14bqldCBr4bq/dCBxdeG6oyB2w6Agc3V5IGRp4buFbiB0aOG7kW5nIGvDqiBk4buFIGTDoG5nIG5oxrAgb3V0cHV0IGPhu6dhIHBhY2thZ2UgbG1lNCBoYXkgbmxtZS4gxJDDonkgbMOgIG5oxrDhu6NjIMSRaeG7g20gY+G7p2EgZ2FtbHNzLg0KDQpW4bqteSBraGkgbsOgbyB0YSBuw6puIGTDuW5nIGdhbWxzczogY2jhu4kga2hpIGLhuqFuIGfhurdwIHbhuqVuIMSR4buBIGtow7MgduG7gSBwaMOibiBwaOG7kWkgKHbDrCBnYW1sc3MgY2hvIHBow6lwIG3DtCBow6xuaCBow7NhIG5oaeG7gXUgaOG7jSBwaMOibiBwaOG7kWkgaMahbiBi4bqldCBj4bupIHBhY2thZ2UgbsOgbyBraMOhYykuIFRo4buxYyByYSBOaGkgbHXDtG4gZMO5bmcgbG1lIHbDoCBubG1lIGNobyBtaXhlZCBtb2RlbCBuaMawbmcgY2jhu4kgbeG7m2kgZMO5bmcgxJHhur9uIGdhbWxzcyAyIGzhuqduIGR1eSBuaOG6pXQsIG3hu5l0IGzhuqduIGzDoCBjaG8gMSBsb25naXR1ZGluYWwgc3R1ZHkgY2hvIDEgZW0gYsOhYyBzxKkgbuG7mWkgdHLDuiBiw6puIFZOICB2w6wgYmnhur9uIHPhu5Ega+G6v3QgcXXhuqMgY+G7p2EgZW0g4bqleSBjw7MgcGjDom4gcGjhu5FpIEdhbW1hOyBt4buZdCBs4bqnbiBraMOhYyBsw6AgY2hvIG3hu5l0IG5naGnDqm4gY+G7qXUgbMOibSBzw6BuZyB24bubaSAxMCBiaeG6v24gc+G7kSBr4bq/dCBxdeG6oyBsw6AgY291bnQgZGF0YSBjw7MgcGjDom4gcGjhu5FpIHplcm8gaW5mbGF0ZWQgbmVnYXRpdmUgYmlub21pYWwuIA0KDQoqKlhpbiBj4bqjbSDGoW4gdsOgIGjhurluIGfhurdwIGzhuqFpLioqDQo=