
Giới thiệu
So sánh giá trị một đại lượng giữa nhiều phân nhóm độc lập là một vấn
đề thường gặp trong nghiên cứu y học lâm sàng. Giải pháp thường quy là
phân tích phương sai một yếu tố (đơn biến) (One-way ANOVA). Tuy nhiên,
ANOVA cổ điển lại đặt ra giả định về tính phân phối chuẩn của dữ liệu và
giả định này gần như không phù hợp với thực tiễn, bởi vì hầu hết những
đại lượng sinh lý bệnh học trong tự nhiên đều có phân phối lệch
dương.
Ở đây chúng tôi giới thiệu một giải pháp linh hoạt và phổ quát hơn
cho bài toán so sánh giá trị trung bình của biến định lượng giữa nhiều
phân nhóm độc lập, đó là mô hình tuyến tính tỗng quát (Generalized
linear model, GLM) với phân phối Gamma.
Công cụ cần thiết:
Quy trình phân tích cần những công cụ sau đây:
Hệ sinh thái tidyverse: với thư viện ggplot2 cho đồ họa thống kê
và dplyr để thao tác dữ liệu
Thư viện ggridges : để vẽ biểu đổ mật độ phân phối riêng cho từng
phân nhóm (ggplot2 cơ bản không hỗ trợ loại biểu đồ này).
Thư viện fitdistrplus cho phép kiểm tra tính phù hợp giữa dữ liệu
thực nghiệm và một quy luật phân phối xác suất lý thuyết, như Gaussian
hoặc Gamma.
Thư viện gamlss: cho phép khớp các mô hình hồi quy cho nhiều họ
phân phối khác nhau, bao gồm Gamma.
Thư viện broom: cho phép thao tác trên kết quả mô hình hồi quy
gamlss.
Thư viện marginaleffects: dùng cho phân tích hậu kiểm (post-hoc
analysis) trên kết quả mô hình hồi quy gamlss
Dữ liệu đầu vào có cấu trúc gồm 1 biến kết quả Y thuộc loại định
lượng liên tục (kiểu dữ liệu double, numeric, int) và 1 biến phân nhóm X
thuộc loại định tính rời rạc (định dạng character hoặc factor). Lưu ý,
phân phối Gamma có miền xác định là tập số thực dương R+, nên trong dữ
liệu Y không được có giá trị < 0 hoặc = 0.
# Thao tác dữ liệu và đồ họa
library(tidyverse)
library(ggridges)
# Kiểm tra quy luật phân phối
library(fitdistrplus)
# Mô hình GLM
library(broom)
library(gamlss)
library(marginaleffects)
Thí dụ minh họa sử dụng dataset “Five_protocols.csv” từ một nghiên
cứu có thực trên 1005 phụ nữ được thực hiện thụ tinh nhân tạo. Mục tiêu
đặt ra là khảo sát giá trị hormone LH ở ngày trigger (biến LH) giữa 5
loại phác đồ kích thích buồng trứng (biến Protocol), gồm:
- Mild = mild stimulation sử dụng Letrozole,
- GnRH_ant = Phác đồ đối vận GnRH,
- Fol_long = Follicular long acting,
- PPOS: Progestin-Primed Ovarian Stimulation,
- Lut_short = Lutean phase short acting.
Chuẩn bị dữ liệu
Dữ liệu được lưu ở định dạng .csv với dấu ngăn cách là “;” và dấu
thập phân là “.”
Ta dùng hàm read.csv để tải dữ liệu vào R và lưu trong dataframe
df.
Dùng hàm na.omit() để loại bỏ những đơn vị quan sát bị thiếu sót
dữ liệu (missing value)
Chuyển Protocol từ 1 biến kiểu kí tự (character) thành factor, để
tương thích với quy trình phân tích.
df <- read.csv('Five_protocols.csv',
sep = ';',
dec= '.',
fileEncoding = 'UTF-8-BOM')%>%na.omit()
df$Protocol = as.factor(df$Protocol)
df%>%sample_frac(0.1)%>%head()%>%knitr::kable()
Lut_short |
40 |
23.00 |
3.26 |
0.05 |
Lut_short |
30 |
22.70 |
1.97 |
0.69 |
GnRH_ant |
30 |
24.20 |
1.55 |
0.19 |
Fol_long |
36 |
20.20 |
0.69 |
0.56 |
Lut_short |
36 |
20.80 |
2.95 |
0.38 |
PPOS |
39 |
20.05 |
1.87 |
0.60 |
Một cách mặc định, biến rời rạc loại factor sẽ được phân cấp theo thứ
tự alphabet, mỗi bậc giá trị sẽ tương ứng với số thứ tự. Trong trường
hợp này, thứ tự các loại protocol là:
1 = Fol_long (sẽ được xem là nhóm tham chiếu khi phân tích hồi
quy)
2 = GnRH_ant
3 = Lut_short
4 = Mild
5 = PPOS
levels(df$Protocol)
## [1] "Fol_long" "GnRH_ant" "Lut_short" "Mild" "PPOS"
Kế hoạch phân tích
Một quy trình phân tích gồm 4 bước sẽ được tiến hành nhằm giải đáp
câu hỏi nghiên cứu đã đặt ra:
Đặc tính phân bố của giá trị LH ở 5 nhóm phác đồ sẽ được mô tả
bằng trung bình, độ lệch chuẩn, trung vị và bách phân vị 5-95, đồng thời
trình bày trực quan bằng biểu đồ mật độ phân bố (Kernel density plot)
hoặc Boxplot, violin plot.
Dữ liệu LH sẽ được khớp lần lượt với 2 quy luật phân phối Gamma
và Gaussian, sau đó đối chiếu đồ thị hàm PDF và CDF để xác nhận tính phù
hợp tốt hơn của phân phối Gamma so với Gaussian.
Mô hình hồi quy tuyến tính tổng quát với phân phối Gamma sẽ được
áp dụng để khảo sát giá trị trung bình LH giữa 5 phác đồ.
Hậu kiểm so sánh bắt cặp tuần tự nhằm kiểm tra một loạt giả định
về sự khác biệt của giá trị LH giữa các nhóm phác đồ.
Suy diễn thống kê dựa trên kiểm định giả thuyết vô hiệu với ngưỡng ý
nghĩa thống kê p = 0.05. Hiệu chỉnh Bonferroni được áp dụng khi kiểm tra
hàng loạt giả định.
Bước 1: Phân tích mô
tả
Mục tiêu của công đoạn này nhằm thăm dò và mô tả sơ khởi về đặc tính
phân phối của LH giữa các nhóm phác đồ khác nhau. Nhận định rút ra từ
phân tích mô tả này cho phép đặt ra những giả thuyết cần chứng minh và
đề xuất hướng phân tích phù hợp.
- Dựng bảng thống kê mô tả
Sử dụng hàm group_by của thư viện dplyr để phân chia dữ liệu
thành 5 phần, tương ứng 5 bậc giá trị của biến Protocol
Áp dụng hàm summarize của thư viện dplyr để tính mean, sd,
median, p5, p95, min và max của LH cho mỗi nhóm nhỏ
Dùng hàm arrange của dplyr để Xếp thứ tự theo giá trị trung bình
LH tăng dần
Kết quả như sau:
df%>%
group_by(Protocol)%>%
summarize(n = n(),
mean = sprintf("%0.2f", mean(LH)),
sd = sprintf("%0.2f",sd(LH)),
median = sprintf("%0.2f", median(LH)),
p5 = sprintf("%0.2f", quantile(LH, 0.05)),
p95 = sprintf("%0.2f", quantile(LH, 0.95)),
min= sprintf("%0.2f", min(LH)),
max = sprintf("%0.2f", max(LH)),
)%>%
arrange(mean)%>%
knitr::kable()
Fol_long |
261 |
1.37 |
1.64 |
0.87 |
0.13 |
4.18 |
0.10 |
12.72 |
Mild |
75 |
12.02 |
8.45 |
8.70 |
2.52 |
27.65 |
1.81 |
36.90 |
Lut_short |
220 |
2.50 |
1.65 |
2.12 |
0.75 |
4.92 |
0.17 |
14.51 |
PPOS |
204 |
3.65 |
3.90 |
2.41 |
0.65 |
10.68 |
0.09 |
29.15 |
GnRH_ant |
245 |
7.78 |
6.01 |
6.05 |
1.55 |
19.06 |
0.41 |
45.02 |
Theo kết quả này, có sự khác biệt về giá trị LH trung bình giữa 5
phác đồ. Một cách cụ thể, LH trung bình có khuynh hướng tăng dần theo
thứ tự:
Fol_long < Short < Mild < < PPOS < GnRH_ant <
Mild
- So sánh trực quan bằng biểu đồ
Ta sẽ sử dụng 2 loại biểu đồ cho phép so sánh đặc điểm phân bố của LH
giữa 5 phác đồ, đó là Biểu đồ mật độ phân phối 1 chiều (Density plot)
hoặc biểu đồ Boxplot
Đầu tiên, ta tạo dataframe med_df trình bày giá trị trung bình của LH
giữa 5 phân nhóm, sử dụng kết hợp 2 hàm group_by và summarize_at.
Dataframe này sẽ được dùng để vẽ biểu đồ tuyến kí và định vị trung bình
LH cho mỗi nhóm.
Tiếp theo, ta lần lượt dùng các hàm:
geom_density_ridges (data = df) để vẽ 5 biểu đồ density plot cho
LH ở 5 nhóm, màu nền theo nhóm phác đồ. Lưu ý rằng hàm
geom_density_ridges mặc định trục x = biến định lượng, y = phân
nhóm.
geom_path cho med_df để vẽ biểu đồ tuyến kí nối các điểm giá trị
LH trung bình;
geom_point cho med_df để chấm 5 điểm giá trị LH trung
bình
Lưu ý rằng trục X luôn là LH, y luôn là Protocol cho 3 hàm trên
Sau đó, dùng hàm coord_flip() để xoay trục 90°, như vậy trục x và y
sẽ hoán chuyển cho nhau, hình ảnh sẽ biểu diễn theo chiều dọc thay vì
chiều ngang.
Hàm scale_x_continuous cho phép tùy chỉnh thang đo trục X, ở đây giới
hạn limits từ -0.5 đến 40, mốc thang đo 5 đon vị
med_df = df %>%
group_by(Protocol)%>%
summarize_at('LH', mean)
ggplot()+
geom_density_ridges(data = df,
aes(y = Protocol,
x = LH,
fill = Protocol),
scale = 0.8,
alpha = 0.5,
show.legend = F)+
geom_path(data = med_df,
aes(y = Protocol, x = LH, group = 1),
color = 'black',
size = 1,
alpha = 0.5)+
geom_point(data = med_df,
aes(y = Protocol, x = LH),
color = 'black',
size = 2.5,
alpha = 0.9)+
coord_flip()+
scale_x_continuous(limits = c(-0.5,40),
breaks = seq(0,40,by = 5))+
labs(x = 'LH level (mIU/mL)', y = 'Protocols')+
theme_bw(10)

Kết quả thu được là 5 biểu đồ mật độ phân bố như trong hình. Các biểu
đồ này có bản chất là đồ thị hàm probability desity (PDF) của biến LH,
trục Y tương ứng với thang đo của LH). Chúng mô tả đặc tính phân phối
của LH trong 5 nhóm phác đồ (trục X). Các chấm màu đen tương ứng với giá
trị trung bình LH ở mỗi nhóm, được nối với nhau bằng các đoạn thẳng thể
hiện khuynh hướng tăng/giảm của giá trị này.
Dựa vào hình ảnh này, ta có thể rút ra được hai thông tin: thứ nhất,
có sự khác biệt rõ nét, không chỉ về giá trị trung bình nhưng còn về đặc
tính phân phối của LH, thí dụ độ phân tán, độ lệch… giữa 5 nhóm. Thứ hai
là ở mỗi phân nhóm, LH chắc chắn không thể phù hợp với quy luật phân
phối chuẩn (Gaussian). Phân phối của LH không đối xứng, thậm chí có độ
lệch rất cao ở các nhóm GnRh_ant, Mild và PPOS.
Ta có thể làm tương tự để tạo ra biểu đổ boxplot, chỉ cần thay hàm
geom_density_ridges bằng hàm geom_boxplot. Lưu ý rằng với dạng biểu đồ
boxplot mặc định thì trục x chỉ Protocol, còn trục y cho LH, ta vẫn dùng
hàm coord_flip() để xoay trục, trình bày theo phương ngang, vì khả năng
so sánh bằng thị giác sẽ tốt hơn theo chiều ngang so với chiều dọc.
ggplot()+
geom_boxplot(data = df,
aes(x = Protocol,
y = LH,
fill = Protocol),
alpha = 0.5,
show.legend = F)+
geom_point(data = med_df,
aes(x = Protocol, y = LH),
color = 'black',
shape = 18,
size = 5,
alpha = 0.9)+
scale_y_continuous(limits = c(-0.5,40),
breaks = seq(0,40,by = 5))+
labs(y = 'LH level (mIU/mL)', x = 'Protocols')+
coord_flip()+
theme_bw(10)

Biểu đồ boxplot trình bày các trị số thống kê mô tả đại diện gồm
trung vị, tứ phân vị (phân vị thứ 25, 50, 75), các chấm tròn nhỏ là
những giá trị vượt ra khỏi khoảng tứ phân vị. Chấm màu đen lớn nhất là
vị trí của trung bình. Tuy không mô tả hình ảnh của toàn thể mật độ phân
phối, biểu đồ Boxplot vừa đủ để cho ta thấy cùng những thông tin về sự
tương phản rõ nét giữa 5 phân nhóm, ta cũng cảm nhận được LH không có
phân phối chuẩn (trung vị không trùng với trung bình, 2 đầu boxplot
không cân xứng, những giá trị cao lệch hẳn về chiều dương của thang đo
LH…).
Bước 2: Biện luận về
tính phù hợp của phân phối Gamma
Mục tiêu của công đoạn này nhằm chứng minh rằng để ước lượng giá trị
LH thì phân phối Gamma phù hợp hơn so với phân phối Gaussian.
Về mặt lý thuyết, ta hoàn toàn có thể chọn quy luật phân phối Gamma
cho biến LH vì những lý do sau: Thang đo LH có giá trị >0, phân phối
của LH có hình ảnh bất đối xứng và lệch dương.
Tuy nhiên, ta có thể dựa vào chứng cứ xác thực hơn bằng quy trình
sau:
Đầu tiên, ta dùng hàm fitfist để lần lượt khớp dữ liệu LH với 2
quy luật phân phối Gaussian và Gamma,
sau đó đối chiếu hình ảnh đồ thị hàm PDF và CDF so với dữ liệu
thực nghiệm, bằng 2 hàm denscomp và cdfcomp:
fit_gam = fitdist(df$LH, 'gamma', optim.method = 'BFGS')
fit_gauss = fitdist(df$LH, 'norm', optim.method = 'BFGS')
denscomp(list(fit_gauss, fit_gam),
legendtext = c("Gaussian", "Gamma"),
plotstyle = 'ggplot',
fitcol = c('#03a9fc','#fc036f'),
dempcol = c('grey'),
fitlty = 1,
fitlwd = 1)+
labs(x = 'LH on trigger day (mUI/mL)')+
theme_bw()

Chú thích: Biểu đồ trên cho phép đối chiếu đồ thị hàm PDF (mật độ xác
suất) của 2 phân phối Gaussian (màu xanh), Gamma (màu đỏ) với hình ảnh
biểu đồ tần suất trên thực nghiệm (màu xám).
cdfcomp(list(fit_gauss, fit_gam),
legendtext = c("Gaussian", "Gamma"),
plotstyle = 'ggplot',
fitcol = c('#03a9fc','#fc036f'),
fitlty = 1,
datacol = 'grey')+
labs(x = 'LH on trigger day (mUI/mL)')+
theme_bw()

Chú thích: Biểu đồ trên cho phép đối chiếu đồ thị hàm CDF (mật độ xác
suất tích lũy) của 2 phân phối Gaussian (màu xanh), Gamma (màu đỏ) với
hàm CDF trên thực nghiệm (màu xám).
Cả 2 kết quả này cho thấy phân phối Gamma thích hợp hơn so với phân
phối Gaussian để ước lượng giá trị LH, vì hàm PDF và CDF của phân phối
Gamma phù hợp hơn với dữ liệu thực nghiệm.
Lợi ích của việc chọn quy luật phân phối đúng cho biến kết quả mô
hình hồi quy, đó là nó cho phép ước lượng và suy luận thống kê chính xác
hơn, phù hợp thực tế hơn.
Bước 3: Dựng mô hình
hồi quy với phân phối Gamma
Như đã trình bày trong kế hoạch phân tích, công cụ thống kê chính mà
ta sử dụng là một mô hình hồi quy tuyến tính tổng quát (GLM). Hệ thống
mô hình GLM do 2 nhà thống kê John Nelder và Robert Wedderburn thiết lập
vào năm 1972 cho phép ước lượng giá trị của biến kết quả theo nhiều quy
luật phân phối đa dạng.
Một cách tổng quát, mô hình GLM ước lượng giá trị trung bình (tham số
\(\mu\)) của biến ngẫu nhiên Y, điều
kiện hóa theo một tập biến X thông qua một hàm liên kết \(g\) và một mô hình hồi tuyến tính \(X\beta\).
\[E(Y|X) = \mu_{Y} \sim
g^{-1}(X\beta)\] Trong bài toán hiện thời, biến kết quả Y là giá
trị của LH ngày trigger, được giả định là một biến ngẫu nhiên theo quy
luật phân phối Gamma với 2 tham số \(\mu\) và \(\sigma\)
Hàm mật độ phân phối (PDF) của phân phối Gamma được xác định như
sau:
\[f(y|\mu ,\sigma)=\frac{1}{\left ( \sigma
^{2} \mu \right )^{1/\sigma^{2}}}\frac{y^{\frac{1}{\sigma
^{2}}-1}e^{-y/(\sigma ^{2}\mu )}}{\Gamma \left ( 1/\sigma^{2} \right
)}\]
\(\mu\) có ý nghĩa như trọng tâm
(central tendency) hay giá trị trung bình của biến kết quả cần ước
lượng, còn giá trị của \(\sigma\) tương
ứng với mức độ phân tán và xác định hình ảnh của hàm mật độ phân phối.
Một khi có mu và sigma, có thể ước tính độ lệch chuẩn (và phương sai)
của LH theo công thức:
\[SD = \sigma * \mu\]
Phân tích hồi quy Gamma dùng thư viện gamlss. Đầu tiên, ta khớp một
mô hình hồi quy Gamma chỉ chứa Intercept. Mô hình này tương ứng với việc
ước lượng giá trị trung bình của LH cho toàn bộ mẫu khảo sát, không phân
biệt nhóm phác đồ nào.
Ta dùng hàm gamlss, với công thức formula = LH ~ 1, và family =
GA
# Mô hình chỉ có intercept
m0 = gamlss(formula = LH ~ 1,
data=df,
family = GA,
trace=F,
parallel="multicore",
ncpus = nC)
Ta sẽ dùng mô hình cơ bản này để làm tham chiếu và so sánh với mô
hình chính trong phân tích, nhằm chứng minh rằng yếu tố chia nhóm theo
phác đồ thực sự có ảnh hưởng đến giá trị trung bình LH.
Nội dung mô hình cơ bản (chỉ chứa Intercept, ước lượng LH trung bình
cho toàn quần thể) có nội dung như sau:
summary(m0)
## ******************************************************************
## Family: c("GA", "Gamma")
##
## Call: gamlss(formula = LH ~ 1, family = GA, data = df, trace = F,
## parallel = "multicore", ncpus = nC)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: log
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.49042 0.03189 46.73 <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.01101 0.01960 0.561 0.575
##
## ------------------------------------------------------------------
## No. of observations in the fit: 1005
## Degrees of Freedom for the fit: 2
## Residual Deg. of Freedom: 1003
## at cycle: 2
##
## Global Deviance: 5005.425
## AIC: 5009.425
## SBC: 5019.251
## ******************************************************************
gamlss dựng 2 mô hình hồi quy riêng cho \(\mu\) và \(\sigma\), cả 2 mô hình đều sử dụng hàm liên
kết là logarit, vì vậy thang đo của các hệ số hồi quy hiện thời là
log(LH), để đưa về thang đo nguyên thủy (mUI/mL) ta phải dùng hàm
exponential.
Mô hình thứ nhất cho biết giá trị trung bình của LH cho toàn quần thể
\(\mu = exp(1.49042) = 4.438959\)
Ta có thể đối chiếu với kết quả ước lượng từ dữ liệu thực nghiệm, và
thấy rằng Intercept của mô hình m0 cơ bản rất phù hợp với giá trị trung
bình của LH
mean(df$LH)
## [1] 4.438955
- Tiếp theo, ta dựng một mô hình khác với 1 biến độc lập là phân nhóm
Protocol. Mô hình này cho phép ước lượng giá trị trung bình LH ở 5 phân
nhóm Protocol khác nhau, hay nói cách khác, khảo sát liên hệ giữa biến
protocol và giá trị trung bình LH (hay hiệu ứng của Protocol đối với giá
trị trung bình của LH).
\[\mu_{LH} \sim \beta_0*Long +
\beta_1*GnRHant + \beta_2*Short + \beta_3*Mild +
\beta_4*PPOS\]
Mô hình này có công thức cho tham số chính (trung bình của phân phối
Gamma) là LH ~ Protocol; và cho tham số sigma: sigma.formula = ~
Protocol.
# Mô hình ANOVA 1 biến
m1 = gamlss(formula = LH ~ Protocol,
sigma.formula = ~ Protocol,
data=df,
family = GA,
trace=F,
parallel="multicore",
ncpus = nC)
Ở đây ta ước lượng đồng thời cả 2 tham số của phân phối Gamma là mu
(trung bình) và sigma (tham số kiểu hình) theo biến Protocol. Vì ta
không chỉ quan tâm đến việc Protocol sẽ làm thay đổi giá trị trung bình
LH (trọng tâm của phân phối Gamma, tham số \(\mu\)), mà còn muốn biết liệu phân loại
Protocol có tác động gây thay đổi hình ảnh (độ phân tán, độ lệch) của
phân phối Gamma (tham số \(\sigma\))
hay không ?
Nội dung của mô hình chính (ước lượng LH theo phác đồ) có nội dung
như sau
summary(m1)
## ******************************************************************
## Family: c("GA", "Gamma")
##
## Call: gamlss(formula = LH ~ Protocol, sigma.formula = ~Protocol,
## family = GA, data = df, trace = F, parallel = "multicore", ncpus = nC)
##
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: log
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.31235 0.05756 5.426 7.23e-08 ***
## ProtocolGnRH_ant 1.73978 0.07409 23.481 < 2e-16 ***
## ProtocolLut_short 0.60523 0.06878 8.800 < 2e-16 ***
## ProtocolMild 2.17429 0.09639 22.556 < 2e-16 ***
## ProtocolPPOS 0.98292 0.08235 11.935 < 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.07264 0.03903 -1.861 0.063003 .
## ProtocolGnRH_ant -0.24182 0.05717 -4.230 2.56e-05 ***
## ProtocolLut_short -0.51025 0.05988 -8.521 < 2e-16 ***
## ProtocolMild -0.32842 0.08573 -3.831 0.000136 ***
## ProtocolPPOS -0.10027 0.05946 -1.686 0.092072 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## No. of observations in the fit: 1005
## Degrees of Freedom for the fit: 10
## Residual Deg. of Freedom: 995
## at cycle: 2
##
## Global Deviance: 4275.111
## AIC: 4295.111
## SBC: 4344.238
## ******************************************************************
Mô hình m1 này cho phép ước lượng giá trị trung bình của LH ở mỗi
phân nhóm Protocol. Lưu ý rằng phác đồ Fol_long được chọn làm tham
chiếu, vì vậy Intercept trong mô hình tương ứng với trung bình của
log(LH) trong phân nhóm Protocol = Fol_long.
Ta có thể đối chiếu với kết quả thống kê mô tả ban đầu:
df%>%
group_by(Protocol)%>%
summarize('mean' = mean(LH)) %>% knitr::kable()
Fol_long |
1.366628 |
GnRH_ant |
7.784449 |
Lut_short |
2.503227 |
Mild |
12.020800 |
PPOS |
3.651961 |
exp(Intercept) tương ứng với trung bình của LH trong phác đồ
Follicular long acting, đơn vị mUI/mL: \(exp(0.31235) = 1.366633\), ta thấy giá trị
này tương đồng với kết quả mean LH ở nhóm Fol_Long
Giá trị trung bình LH nhóm GnRH_ant được tính bằng : exp(Intercept +
ProtocolGnRH_ant): \(exp(0.31235 + 1.73978) =
7.784464\) …
Cứ thế, ta có thể ước lượng được trung bình của log(LH) hoặc LH ở cả
5 phân nhóm.
Bước 4: Suy diễn thống
kê
Dựa vào 2 mô hình tuyến tính tổng quát này, ta có thể thực hiện một
số suy diễn thống kê như sau:
Đầu tiên, khi so sánh mô hình chỉ chứa intercept và mô hình có thêm
biến Protocol bằng một kiểm định Likelihood ratio, ta có thể biết liệu
phân nhóm phác đồ thực sự có hiệu ứng ý nghĩa đối với LH hay không ?
(câu hỏi này tương tự với kiểm định F theo Fisher trong phân tích phương
sai cổ điển)
LR.test(m0,m1)
## Likelihood Ratio Test for nested GAMLSS models.
## (No check whether the models are nested is performed).
##
## Null model: deviance= 5005.425 with 2 deg. of freedom
## Altenative model: deviance= 4275.111 with 10 deg. of freedom
##
## LRT = 730.3143 with 8 deg. of freedom and p-value= 0
Kết quả cho ra một giá trị p cực kì thấp, như vậy ta có thể khẳng
định rằng phân nhóm phác đồ thực sự có tác động làm thay đổi LH.
Nói cách khác, có sự khác biệt ý nghĩa về giá trị LH giữa các nhóm
phác đồ khác nhau. Tuy nhiên ta chưa thể biết rõ sự khác biệt này là ở
nhóm nào so với nhóm nào.
Nội dung hệ số hồi quy trong mô hình cho phép ta kiểm định về sự khác
biệt của LH trung bình ở phác đồ PPOS, GnRH, Luteal short acting và Mild
(Letrozole) so với nhóm tham chiếu là Follicular long acting.
Dựa vào kết quả thô của mô hình, có thể thấy tất cả hệ số hồi quy đều
>0 và có ý nghĩa thống kê, cho thấy giá trị trung bình LH ở 4 phác đồ
còn lại đều cao hơn so với phác đồ tham chiếu (Longacting).
Tuy nhiên, ta sẽ không suy diễn trực tiếp dựa vào hệ số hồi quy, mà
tiến hành một phân tích hậu kiểm với nội dung so sánh bắt cặp tuần tự
giữa 5 loại phác đồ. Trong khi làm thống kê mô tả, ta có cảm nhận về
khuynh hướng tăng dần của giá trị LH trung bình theo thứ tự: Fol_long
< Short < PPOS < GnRH_ant < Mild
Ở đây ta sẽ kiểm tra lại kết quả này.
Để tiến hành phân tích hậu kiểm này, ta dùng hàm avg_comparisons của
thư viện marginalseffect với tùy chỉnh variables = list(Protocol =
‘pairwise’), sau đó hiệu chỉnh giá trị p bằng hàm p.adjust trong R, với
phương pháp hiệu chỉnh Bonferroni.
Kết quả của hàm này là một bảng như sau:
pw_comp = avg_comparisons(m1,
what = 'mu',
variables = list(Protocol = 'pairwise'))
pw_comp$adj_p = p.adjust(pw_comp$p.value, method = 'bonferroni')
pw_comp %>% dplyr::select(-c(1,2,6,7)) %>%knitr::kable()
GnRH_ant - Fol_long |
6.417821 |
0.3715848 |
5.6895277 |
7.146114 |
0.0000000 |
Lut_short - Fol_long |
1.136599 |
0.1227487 |
0.8960158 |
1.377182 |
0.0000000 |
Lut_short - GnRH_ant |
-5.281222 |
0.3751863 |
-6.0165734 |
-4.545870 |
0.0000000 |
Mild - Fol_long |
10.654172 |
0.9328177 |
8.8258825 |
12.482461 |
0.0000000 |
Mild - GnRH_ant |
4.236351 |
0.9979211 |
2.2804617 |
6.192240 |
0.0002184 |
Mild - Lut_short |
9.517573 |
0.9342582 |
7.6864603 |
11.348685 |
0.0000000 |
PPOS - Fol_long |
2.285332 |
0.2290351 |
1.8364319 |
2.734233 |
0.0000000 |
PPOS - GnRH_ant |
-4.132488 |
0.4220836 |
-4.9597569 |
-3.305220 |
0.0000000 |
PPOS - Lut_short |
1.148734 |
0.2348331 |
0.6884692 |
1.608998 |
0.0000100 |
PPOS - Mild |
-8.368839 |
0.9540588 |
-10.2387601 |
-6.498918 |
0.0000000 |
Cột contrast trình bày các cặp so sánh mà ta cần kiểm nghiệm, thí dụ
GnRH_ant - Fol_long;
Cột estimate trình bày giá trị khác biệt trung bình về LH giữa 2 nhóm
phác đồ, thí dụ GnRHant cao hơn Long acting 6.4178 mUI/mL.
2 cột conf.low conf.high trình bày ngưỡng dưới và trên khoảng tin cậy
95% của khác biệt, KTC95% có ý nghĩa thống kê khi không chứa giá trị
0.
Cột adj_p trình bày giá trị p sau hiệu chỉnh Bonferroni,
Kết quả cho thấy toàn bộ 10 giả thuyết đều có ý nghĩa thống kê. Một
cách tổng quát, giá trị LH trung bình tăng dần theo thứ tự 5 phác
đồ:
Fol_long < Short < PPOS < GnRH_ant < Mild
Như vậy phác đồ Mild stimulation với Letrozole có LH trung bình ngày
trigger cao nhất, phác đồ Follicular long acting có LH trung bình thấp
nhất, phác đồ PPOS ở vị trí trung gian, với LH cao hơn so với 2 phác đồ
Long và Short.
Cuối cùng, kết quả của mô hình hồi quy có thể trình bày trực quan như
sau:
m1%>%augment()%>%mutate(UL= .fitted +.se.fit*1.96,
LL=.fitted-.se.fit*1.96)%>%
group_by(Protocol)%>%
summarize_at('.fitted', median) -> m1_sum
augment(m1)%>%mutate(UL= .fitted +.se.fit*1.96,
LL=.fitted-.se.fit*1.96)%>%
ggplot()+
geom_jitter(aes(y = Protocol,
x = LH,
col = Protocol),
scale = 0.8,
alpha = 0.3,
width = 0.01,
show.legend = F)+
geom_density_ridges(aes(y = Protocol,
x = LH,
fill = Protocol),
scale = 0.8,
alpha = 0.3,
show.legend = F)+
geom_errorbar(aes(y=Protocol,
xmin=exp(LL),
xmax=exp(UL)),
width=0.1,
size=1) +
geom_path(data = m1_sum,
aes(y=Protocol,
x=exp(.fitted),
group = 1))+
geom_point(aes(y=Protocol,
x=exp(.fitted)),
size=3)+
scale_x_continuous(breaks = seq(0,40,by = 5))+
labs(x = 'LH level on trigger day (mIU/mL)', y = 'Protocols')+
coord_flip()+
theme_bw()

Kết luận
Ta thấy mô hình GLM với phân phối Gamma là một giải pháp phổ quát và
hiệu quả cho hầu hết những tình huống nghiên cứu y học lâm sàng. Mô hình
GLM Gamma có thể thay thế hoàn toàn phân tích phương sai (ANOVA) cổ điển
và cả mô hình hồi quy tuyến tính thông thường.
LS0tDQp0aXRsZTogIk3DtCBow6xuaCBo4buTaSBxdXkgR2FtbWEiDQphdXRob3I6ICJCUy4gTmfhu41jIENow6J1LCBCUy4gS2jhuqMgTmhpIg0KZGF0ZTogIjI3IFRow6FuZyAyIG7Eg20gMjAyMyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBkZWZhdWx0DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGRldjogc3ZnDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIGxhdGV4X2VuZ2luZTogbHVhbGF0ZXgNCiAgICBrZWVwX3RleDogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0UpDQpgYGANCg0KIVtdKEdhbW1hX0dMTS5wbmcpDQoNCiMgR2nhu5tpIHRoaeG7h3UNCg0KU28gc8OhbmggZ2nDoSB0cuG7iyBt4buZdCDEkeG6oWkgbMaw4bujbmcgZ2nhu69hIG5oaeG7gXUgcGjDom4gbmjDs20gxJHhu5ljIGzhuq1wIGzDoCBt4buZdCB24bqlbiDEkeG7gSB0aMaw4budbmcgZ+G6t3AgdHJvbmcgbmdoacOqbiBj4bupdSB5IGjhu41jIGzDom0gc8OgbmcuIEdp4bqjaSBwaMOhcCB0aMaw4budbmcgcXV5IGzDoCBwaMOibiB0w61jaCBwaMawxqFuZyBzYWkgbeG7mXQgeeG6v3UgdOG7kSAoxJHGoW4gYmnhur9uKSAoT25lLXdheSBBTk9WQSkuIFR1eSBuaGnDqm4sIEFOT1ZBIGPhu5UgxJFp4buDbiBs4bqhaSDEkeG6t3QgcmEgZ2nhuqMgxJHhu4tuaCB24buBIHTDrW5oIHBow6JuIHBo4buRaSBjaHXhuqluIGPhu6dhIGThu68gbGnhu4d1IHbDoCBnaeG6oyDEkeG7i25oIG7DoHkgZ+G6p24gbmjGsCBraMO0bmcgcGjDuSBo4bujcCB24bubaSB0aOG7sWMgdGnhu4VuLCBi4bufaSB2w6wgaOG6p3UgaOG6v3Qgbmjhu69uZyDEkeG6oWkgbMaw4bujbmcgc2luaCBsw70gYuG7h25oIGjhu41jIHRyb25nIHThu7Egbmhpw6puIMSR4buBdSBjw7MgcGjDom4gcGjhu5FpIGzhu4djaCBkxrDGoW5nLg0KDQrhu54gxJHDonkgY2jDum5nIHTDtGkgZ2nhu5tpIHRoaeG7h3UgbeG7mXQgZ2nhuqNpIHBow6FwIGxpbmggaG/huqF0IHbDoCBwaOG7lSBxdcOhdCBoxqFuIGNobyBiw6BpIHRvw6FuIHNvIHPDoW5oIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggY+G7p2EgYmnhur9uIMSR4buLbmggbMaw4bujbmcgZ2nhu69hIG5oaeG7gXUgcGjDom4gbmjDs20gxJHhu5ljIGzhuq1wLCDEkcOzIGzDoCBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buXbmcgcXXDoXQgKEdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCwgR0xNKSB24bubaSBwaMOibiBwaOG7kWkgR2FtbWEuDQoNCiMgQ8O0bmcgY+G7pSBj4bqnbiB0aGnhur90Og0KDQpRdXkgdHLDrG5oIHBow6JuIHTDrWNoIGPhuqduIG5o4buvbmcgY8O0bmcgY+G7pSBzYXUgxJHDonk6DQoNCisgSOG7hyBzaW5oIHRow6FpIHRpZHl2ZXJzZTogduG7m2kgdGjGsCB2aeG7h24gZ2dwbG90MiBjaG8gxJHhu5MgaOG7jWEgdGjhu5FuZyBrw6ogdsOgIGRwbHlyIMSR4buDIHRoYW8gdMOhYyBk4buvIGxp4buHdQ0KDQorIFRoxrAgdmnhu4duIGdncmlkZ2VzIDogxJHhu4MgduG6vSBiaeG7g3UgxJHhu5UgbeG6rXQgxJHhu5kgcGjDom4gcGjhu5FpIHJpw6puZyBjaG8gdOG7q25nIHBow6JuIG5ow7NtIChnZ3Bsb3QyIGPGoSBi4bqjbiBraMO0bmcgaOG7lyB0cuG7oyBsb+G6oWkgYmnhu4N1IMSR4buTIG7DoHkpLg0KDQorIFRoxrAgdmnhu4duIGZpdGRpc3RycGx1cyBjaG8gcGjDqXAga2nhu4NtIHRyYSB0w61uaCBwaMO5IGjhu6NwIGdp4buvYSBk4buvIGxp4buHdSB0aOG7sWMgbmdoaeG7h20gdsOgIG3hu5l0IHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIHjDoWMgc3XhuqV0IGzDvSB0aHV54bq/dCwgbmjGsCBHYXVzc2lhbiBob+G6t2MgR2FtbWEuDQoNCisgVGjGsCB2aeG7h24gZ2FtbHNzOiBjaG8gcGjDqXAga2jhu5twIGPDoWMgbcO0IGjDrG5oIGjhu5NpIHF1eSBjaG8gbmhp4buBdSBo4buNIHBow6JuIHBo4buRaSBraMOhYyBuaGF1LCBiYW8gZ+G7k20gR2FtbWEuDQoNCisgVGjGsCB2aeG7h24gYnJvb206IGNobyBwaMOpcCB0aGFvIHTDoWMgdHLDqm4ga+G6v3QgcXXhuqMgbcO0IGjDrG5oIGjhu5NpIHF1eSBnYW1sc3MuDQoNCisgVGjGsCB2aeG7h24gbWFyZ2luYWxlZmZlY3RzOiBkw7luZyBjaG8gcGjDom4gdMOtY2ggaOG6rXUga2nhu4NtIChwb3N0LWhvYyBhbmFseXNpcykgdHLDqm4ga+G6v3QgcXXhuqMgbcO0IGjDrG5oIGjhu5NpIHF1eSBnYW1sc3MNCg0KKyBE4buvIGxp4buHdSDEkeG6p3UgdsOgbyBjw7MgY+G6pXUgdHLDumMgZ+G7k20gMSBiaeG6v24ga+G6v3QgcXXhuqMgWSB0aHXhu5ljIGxv4bqhaSDEkeG7i25oIGzGsOG7o25nIGxpw6puIHThu6VjIChraeG7g3UgZOG7ryBsaeG7h3UgZG91YmxlLCBudW1lcmljLCBpbnQpIHbDoCAxIGJp4bq/biBwaMOibiBuaMOzbSBYIHRodeG7mWMgbG/huqFpIMSR4buLbmggdMOtbmggcuG7nWkgcuG6oWMgKMSR4buLbmggZOG6oW5nIGNoYXJhY3RlciBob+G6t2MgZmFjdG9yKS4gTMawdSDDvSwgcGjDom4gcGjhu5FpIEdhbW1hIGPDsyBtaeG7gW4geMOhYyDEkeG7i25oIGzDoCB04bqtcCBz4buRIHRo4buxYyBkxrDGoW5nIFIrLCBuw6puIHRyb25nIGThu68gbGnhu4d1IFkga2jDtG5nIMSRxrDhu6NjIGPDsyBnacOhIHRy4buLIDwgMCBob+G6t2MgPSAwLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCiMgVGhhbyB0w6FjIGThu68gbGnhu4d1IHbDoCDEkeG7kyBo4buNYQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdncmlkZ2VzKQ0KDQojIEtp4buDbSB0cmEgcXV5IGx14bqtdCBwaMOibiBwaOG7kWkNCmxpYnJhcnkoZml0ZGlzdHJwbHVzKQ0KDQojIE3DtCBow6xuaCBHTE0NCmxpYnJhcnkoYnJvb20pDQpsaWJyYXJ5KGdhbWxzcykNCg0KbGlicmFyeShtYXJnaW5hbGVmZmVjdHMpDQpgYGANCg0KVGjDrSBk4bulIG1pbmggaOG7jWEgc+G7rSBk4bulbmcgZGF0YXNldCAiRml2ZV9wcm90b2NvbHMuY3N2IiB04burIG3hu5l0IG5naGnDqm4gY+G7qXUgY8OzIHRo4buxYyB0csOqbiAxMDA1IHBo4bulIG7hu68gxJHGsOG7o2MgdGjhu7FjIGhp4buHbiB0aOG7pSB0aW5oIG5ow6JuIHThuqFvLiBN4bulYyB0acOqdSDEkeG6t3QgcmEgbMOgIGto4bqjbyBzw6F0IGdpw6EgdHLhu4sgaG9ybW9uZSBMSCDhu58gbmfDoHkgdHJpZ2dlciAoYmnhur9uIExIKSBnaeG7r2EgNSBsb+G6oWkgcGjDoWMgxJHhu5Mga8OtY2ggdGjDrWNoIGJ14buTbmcgdHLhu6luZyAoYmnhur9uIFByb3RvY29sKSwgZ+G7k206IA0KDQorIE1pbGQgPSBtaWxkIHN0aW11bGF0aW9uIHPhu60gZOG7pW5nIExldHJvem9sZSwgDQorIEduUkhfYW50ID0gUGjDoWMgxJHhu5MgxJHhu5FpIHbhuq1uIEduUkgsIA0KKyBGb2xfbG9uZyA9IEZvbGxpY3VsYXIgbG9uZyBhY3RpbmcsIA0KKyBQUE9TOiBQcm9nZXN0aW4tUHJpbWVkIE92YXJpYW4gU3RpbXVsYXRpb24sIA0KKyBMdXRfc2hvcnQgPSBMdXRlYW4gcGhhc2Ugc2hvcnQgYWN0aW5nLg0KDQojIENodeG6qW4gYuG7iyBk4buvIGxp4buHdQ0KDQpE4buvIGxp4buHdSDEkcaw4bujYyBsxrB1IOG7nyDEkeG7i25oIGThuqFuZyAuY3N2IHbhu5tpIGThuqV1IG5nxINuIGPDoWNoIGzDoCAiOyIgdsOgIGThuqV1IHRo4bqtcCBwaMOibiBsw6AgIi4iDQoNCjEpIFRhIGTDuW5nIGjDoG0gcmVhZC5jc3YgxJHhu4MgdOG6o2kgZOG7ryBsaeG7h3UgdsOgbyBSIHbDoCBsxrB1IHRyb25nIGRhdGFmcmFtZSBkZi4NCg0KMikgRMO5bmcgaMOgbSBuYS5vbWl0KCkgxJHhu4MgbG/huqFpIGLhu48gbmjhu69uZyDEkcahbiB24buLIHF1YW4gc8OhdCBi4buLIHRoaeG6v3Ugc8OzdCBk4buvIGxp4buHdSAobWlzc2luZyB2YWx1ZSkNCg0KMykgQ2h1eeG7g24gUHJvdG9jb2wgdOG7qyAxIGJp4bq/biBraeG7g3Uga8OtIHThu7EgKGNoYXJhY3RlcikgdGjDoG5oIGZhY3RvciwgxJHhu4MgdMawxqFuZyB0aMOtY2ggduG7m2kgcXV5IHRyw6xuaCBwaMOibiB0w61jaC4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpkZiA8LSByZWFkLmNzdignRml2ZV9wcm90b2NvbHMuY3N2JywgDQogICAgICAgICAgICAgICBzZXAgPSAnOycsIA0KICAgICAgICAgICAgICAgZGVjPSAnLicsIA0KICAgICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gJ1VURi04LUJPTScpJT4lbmEub21pdCgpDQoNCmRmJFByb3RvY29sID0gYXMuZmFjdG9yKGRmJFByb3RvY29sKQ0KDQpkZiU+JXNhbXBsZV9mcmFjKDAuMSklPiVoZWFkKCklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNCk3hu5l0IGPDoWNoIG3hurdjIMSR4buLbmgsIGJp4bq/biBy4budaSBy4bqhYyBsb+G6oWkgZmFjdG9yIHPhur0gxJHGsOG7o2MgcGjDom4gY+G6pXAgdGhlbyB0aOG7qSB04buxIGFscGhhYmV0LCBt4buXaSBi4bqtYyBnacOhIHRy4buLIHPhur0gdMawxqFuZyDhu6luZyB24bubaSBz4buRIHRo4bupIHThu7EuIFRyb25nIHRyxrDhu51uZyBo4bujcCBuw6B5LCB0aOG7qSB04buxIGPDoWMgbG/huqFpIHByb3RvY29sIGzDoDoNCg0KMSA9IEZvbF9sb25nIChz4bq9IMSRxrDhu6NjIHhlbSBsw6AgbmjDs20gdGhhbSBjaGnhur91IGtoaSBwaMOibiB0w61jaCBo4buTaSBxdXkpDQoNCjIgPSBHblJIX2FudA0KDQozID0gTHV0X3Nob3J0DQoNCjQgPSBNaWxkDQoNCjUgPSBQUE9TDQoNCmBgYHtyfQ0KbGV2ZWxzKGRmJFByb3RvY29sKQ0KYGBgDQojIEvhur8gaG/huqFjaCBwaMOibiB0w61jaA0KDQpN4buZdCBxdXkgdHLDrG5oIHBow6JuIHTDrWNoIGfhu5NtIDQgYsaw4bubYyBz4bq9IMSRxrDhu6NjIHRp4bq/biBow6BuaCBuaOG6sW0gZ2nhuqNpIMSRw6FwIGPDonUgaOG7j2kgbmdoacOqbiBj4bupdSDEkcOjIMSR4bq3dCByYToNCg0KMSkgxJDhurdjIHTDrW5oIHBow6JuIGLhu5EgY+G7p2EgZ2nDoSB0cuG7iyBMSCDhu58gNSBuaMOzbSBwaMOhYyDEkeG7kyBz4bq9IMSRxrDhu6NjIG3DtCB04bqjIGLhurFuZyB0cnVuZyBiw6xuaCwgxJHhu5kgbOG7h2NoIGNodeG6qW4sIHRydW5nIHbhu4sgdsOgIGLDoWNoIHBow6JuIHbhu4sgNS05NSwgxJHhu5NuZyB0aOG7nWkgdHLDrG5oIGLDoHkgdHLhu7FjIHF1YW4gYuG6sW5nIGJp4buDdSDEkeG7kyBt4bqtdCDEkeG7mSBwaMOibiBi4buRIChLZXJuZWwgZGVuc2l0eSBwbG90KSBob+G6t2MgQm94cGxvdCwgdmlvbGluIHBsb3QuDQoNCjIpIEThu68gbGnhu4d1IExIIHPhur0gxJHGsOG7o2Mga2jhu5twIGzhuqduIGzGsOG7o3QgduG7m2kgMiBxdXkgbHXhuq10IHBow6JuIHBo4buRaSBHYW1tYSB2w6AgR2F1c3NpYW4sIHNhdSDEkcOzIMSR4buRaSBjaGnhur91IMSR4buTIHRo4buLIGjDoG0gUERGIHbDoCBDREYgxJHhu4MgeMOhYyBuaOG6rW4gdMOtbmggcGjDuSBo4bujcCB04buRdCBoxqFuIGPhu6dhIHBow6JuIHBo4buRaSBHYW1tYSBzbyB24bubaSBHYXVzc2lhbi4NCg0KMykgTcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCB24bubaSBwaMOibiBwaOG7kWkgR2FtbWEgc+G6vSDEkcaw4bujYyDDoXAgZOG7pW5nIMSR4buDIGto4bqjbyBzw6F0IGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTEggZ2nhu69hIDUgcGjDoWMgxJHhu5MuDQoNCjQpIEjhuq11IGtp4buDbSBzbyBzw6FuaCBi4bqvdCBj4bq3cCB0deG6p24gdOG7sSBuaOG6sW0ga2nhu4NtIHRyYSBt4buZdCBsb+G6oXQgZ2nhuqMgxJHhu4tuaCB24buBIHPhu7Ega2jDoWMgYmnhu4d0IGPhu6dhIGdpw6EgdHLhu4sgTEggZ2nhu69hIGPDoWMgbmjDs20gcGjDoWMgxJHhu5MuDQoNClN1eSBkaeG7hW4gdGjhu5FuZyBrw6ogZOG7sWEgdHLDqm4ga2nhu4NtIMSR4buLbmggZ2nhuqMgdGh1eeG6v3QgdsO0IGhp4buHdSB24bubaSBuZ8aw4buhbmcgw70gbmdoxKlhIHRo4buRbmcga8OqIHAgPSAwLjA1LiBIaeG7h3UgY2jhu4luaCBCb25mZXJyb25pIMSRxrDhu6NjIMOhcCBk4bulbmcga2hpIGtp4buDbSB0cmEgaMOgbmcgbG/huqF0IGdp4bqjIMSR4buLbmguDQoNCiMgQsaw4bubYyAxOiBQaMOibiB0w61jaCBtw7QgdOG6ow0KDQpN4bulYyB0acOqdSBj4bunYSBjw7RuZyDEkW/huqFuIG7DoHkgbmjhurFtIHRoxINtIGTDsiB2w6AgbcO0IHThuqMgc8ahIGto4bufaSB24buBIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkgY+G7p2EgTEggZ2nhu69hIGPDoWMgbmjDs20gcGjDoWMgxJHhu5Mga2jDoWMgbmhhdS4gTmjhuq1uIMSR4buLbmggcsO6dCByYSB04burIHBow6JuIHTDrWNoIG3DtCB04bqjIG7DoHkgY2hvIHBow6lwIMSR4bq3dCByYSBuaOG7r25nIGdp4bqjIHRodXnhur90IGPhuqduIGNo4bupbmcgbWluaCB2w6AgxJHhu4EgeHXhuqV0IGjGsOG7m25nIHBow6JuIHTDrWNoIHBow7kgaOG7o3AuDQoNCjEpIEThu7FuZyBi4bqjbmcgdGjhu5FuZyBrw6ogbcO0IHThuqMNCg0KKyBT4butIGThu6VuZyBow6BtIGdyb3VwX2J5IGPhu6dhIHRoxrAgdmnhu4duIGRwbHlyIMSR4buDIHBow6JuIGNoaWEgZOG7ryBsaeG7h3UgdGjDoG5oIDUgcGjhuqduLCB0xrDGoW5nIOG7qW5nIDUgYuG6rWMgZ2nDoSB0cuG7iyBj4bunYSBiaeG6v24gUHJvdG9jb2wgDQoNCisgw4FwIGThu6VuZyBow6BtIHN1bW1hcml6ZSBj4bunYSB0aMawIHZp4buHbiBkcGx5ciDEkeG7gyB0w61uaCBtZWFuLCBzZCwgbWVkaWFuLCBwNSwgcDk1LCBtaW4gdsOgIG1heCBj4bunYSBMSCBjaG8gbeG7l2kgbmjDs20gbmjhu48NCg0KKyBEw7luZyBow6BtIGFycmFuZ2UgY+G7p2EgZHBseXIgxJHhu4MgWOG6v3AgdGjhu6kgdOG7sSB0aGVvIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTEggdMSDbmcgZOG6p24NCg0KS+G6v3QgcXXhuqMgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KZGYlPiUgDQogIGdyb3VwX2J5KFByb3RvY29sKSU+JQ0KICBzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIG1lYW4gPSBzcHJpbnRmKCIlMC4yZiIsIG1lYW4oTEgpKSwNCiAgICAgICAgICAgIHNkID0gc3ByaW50ZigiJTAuMmYiLHNkKExIKSksDQogICAgICAgICAgICBtZWRpYW4gPSBzcHJpbnRmKCIlMC4yZiIsIG1lZGlhbihMSCkpLA0KICAgICAgICAgICAgcDUgPSBzcHJpbnRmKCIlMC4yZiIsIHF1YW50aWxlKExILCAwLjA1KSksDQogICAgICAgICAgICBwOTUgPSBzcHJpbnRmKCIlMC4yZiIsIHF1YW50aWxlKExILCAwLjk1KSksDQogICAgICAgICAgICBtaW49IHNwcmludGYoIiUwLjJmIiwgbWluKExIKSksDQogICAgICAgICAgICBtYXggPSBzcHJpbnRmKCIlMC4yZiIsIG1heChMSCkpLA0KICAgICAgICAgICAgKSU+JQ0KICAgIGFycmFuZ2UobWVhbiklPiUNCiAgICBrbml0cjo6a2FibGUoKQ0KYGBgDQoNClRoZW8ga+G6v3QgcXXhuqMgbsOgeSwgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IHbhu4EgZ2nDoSB0cuG7iyBMSCB0cnVuZyBiw6xuaCBnaeG7r2EgNSBwaMOhYyDEkeG7ky4gTeG7mXQgY8OhY2ggY+G7pSB0aOG7gywgTEggdHJ1bmcgYsOsbmggY8OzIGtodXluaCBoxrDhu5tuZyB0xINuZyBk4bqnbiB0aGVvIHRo4bupIHThu7E6DQoNCkZvbF9sb25nIDwgU2hvcnQgPCBNaWxkIDwgPCBQUE9TIDwgR25SSF9hbnQgPCBNaWxkDQoNCjIpIFNvIHPDoW5oIHRy4buxYyBxdWFuIGLhurFuZyBiaeG7g3UgxJHhu5MNCg0KVGEgc+G6vSBz4butIGThu6VuZyAyIGxv4bqhaSBiaeG7g3UgxJHhu5MgY2hvIHBow6lwIHNvIHPDoW5oIMSR4bq3YyDEkWnhu4NtIHBow6JuIGLhu5EgY+G7p2EgTEggZ2nhu69hIDUgcGjDoWMgxJHhu5MsIMSRw7MgbMOgIEJp4buDdSDEkeG7kyBt4bqtdCDEkeG7mSBwaMOibiBwaOG7kWkgMSBjaGnhu4F1IChEZW5zaXR5IHBsb3QpIGhv4bq3YyBiaeG7g3UgxJHhu5MgQm94cGxvdA0KDQrEkOG6p3UgdGnDqm4sIHRhIHThuqFvIGRhdGFmcmFtZSBtZWRfZGYgdHLDrG5oIGLDoHkgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBMSCBnaeG7r2EgNSBwaMOibiBuaMOzbSwgc+G7rSBk4bulbmcga+G6v3QgaOG7o3AgMiBow6BtIGdyb3VwX2J5IHbDoCBzdW1tYXJpemVfYXQuIERhdGFmcmFtZSBuw6B5IHPhur0gxJHGsOG7o2MgZMO5bmcgxJHhu4MgduG6vSBiaeG7g3UgxJHhu5MgdHV54bq/biBrw60gdsOgIMSR4buLbmggduG7iyB0cnVuZyBiw6xuaCBMSCBjaG8gbeG7l2kgbmjDs20uDQoNClRp4bq/cCB0aGVvLCB0YSBs4bqnbiBsxrDhu6N0IGTDuW5nIGPDoWMgaMOgbToNCg0KKyBnZW9tX2RlbnNpdHlfcmlkZ2VzIChkYXRhID0gZGYpIMSR4buDIHbhur0gNSBiaeG7g3UgxJHhu5MgZGVuc2l0eSBwbG90IGNobyBMSCDhu58gNSBuaMOzbSwgbcOgdSBu4buBbiB0aGVvIG5ow7NtIHBow6FjIMSR4buTLiBMxrB1IMO9IHLhurFuZyBow6BtIGdlb21fZGVuc2l0eV9yaWRnZXMgbeG6t2MgxJHhu4tuaCB0cuG7pWMgeCA9IGJp4bq/biDEkeG7i25oIGzGsOG7o25nLCB5ID0gcGjDom4gbmjDs20uDQoNCisgZ2VvbV9wYXRoIGNobyBtZWRfZGYgxJHhu4MgduG6vSBiaeG7g3UgxJHhu5MgdHV54bq/biBrw60gbuG7kWkgY8OhYyDEkWnhu4NtIGdpw6EgdHLhu4sgTEggdHJ1bmcgYsOsbmg7DQoNCisgZ2VvbV9wb2ludCBjaG8gbWVkX2RmIMSR4buDIGNo4bqlbSA1IMSRaeG7g20gZ2nDoSB0cuG7iyBMSCB0cnVuZyBiw6xuaA0KDQpMxrB1IMO9IHLhurFuZyB0cuG7pWMgWCBsdcO0biBsw6AgTEgsIHkgbHXDtG4gbMOgIFByb3RvY29sIGNobyAzIGjDoG0gdHLDqm4NCg0KU2F1IMSRw7MsIGTDuW5nIGjDoG0gY29vcmRfZmxpcCgpIMSR4buDIHhvYXkgdHLhu6VjIDkwwrAsIG5oxrAgduG6rXkgdHLhu6VjIHggdsOgIHkgc+G6vSBob8OhbiBjaHV54buDbiBjaG8gbmhhdSwgaMOsbmgg4bqjbmggc+G6vSBiaeG7g3UgZGnhu4VuIHRoZW8gY2hp4buBdSBk4buNYyB0aGF5IHbDrCBjaGnhu4F1IG5nYW5nLg0KDQpIw6BtIHNjYWxlX3hfY29udGludW91cyBjaG8gcGjDqXAgdMO5eSBjaOG7iW5oIHRoYW5nIMSRbyB0cuG7pWMgWCwg4bufIMSRw6J5IGdp4bubaSBo4bqhbiBsaW1pdHMgdOG7qyAtMC41IMSR4bq/biA0MCwgbeG7kWMgdGhhbmcgxJFvIDUgxJFvbiB24buLDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWVkX2RmID0gZGYgJT4lIA0KICBncm91cF9ieShQcm90b2NvbCklPiUNCiAgc3VtbWFyaXplX2F0KCdMSCcsIG1lYW4pDQoNCmdncGxvdCgpKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGRhdGEgPSBkZiwNCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeSA9IFByb3RvY29sLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IExILA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gUHJvdG9jb2wpLA0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMC44LA0KICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LA0KICAgICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIGdlb21fcGF0aChkYXRhID0gbWVkX2RmLA0KICAgICAgICAgICAgYWVzKHkgPSBQcm90b2NvbCwgeCA9IExILCBncm91cCA9IDEpLA0KICAgICAgICAgICAgY29sb3IgPSAnYmxhY2snLA0KICAgICAgICAgICAgc2l6ZSA9IDEsDQogICAgICAgICAgICBhbHBoYSA9IDAuNSkrDQogIGdlb21fcG9pbnQoZGF0YSA9IG1lZF9kZiwNCiAgICAgICAgICAgIGFlcyh5ID0gUHJvdG9jb2wsIHggPSBMSCksDQogICAgICAgICAgICBjb2xvciA9ICdibGFjaycsDQogICAgICAgICAgICBzaXplID0gMi41LA0KICAgICAgICAgICAgYWxwaGEgPSAwLjkpKw0KICBjb29yZF9mbGlwKCkrDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjUsNDApLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLDQwLGJ5ID0gNSkpKw0KICBsYWJzKHggPSAnTEggbGV2ZWwgKG1JVS9tTCknLCB5ID0gJ1Byb3RvY29scycpKw0KICB0aGVtZV9idygxMCkNCmBgYA0KDQpL4bq/dCBxdeG6oyB0aHUgxJHGsOG7o2MgbMOgIDUgYmnhu4N1IMSR4buTIG3huq10IMSR4buZIHBow6JuIGLhu5EgbmjGsCB0cm9uZyBow6xuaC4gQ8OhYyBiaeG7g3UgxJHhu5MgbsOgeSBjw7MgYuG6o24gY2jhuqV0IGzDoCDEkeG7kyB0aOG7iyBow6BtIHByb2JhYmlsaXR5IGRlc2l0eSAoUERGKSBj4bunYSBiaeG6v24gTEgsIHRy4bulYyBZIHTGsMahbmcg4bupbmcgduG7m2kgdGhhbmcgxJFvIGPhu6dhIExIKS4gQ2jDum5nIG3DtCB04bqjIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkgY+G7p2EgTEggdHJvbmcgNSBuaMOzbSBwaMOhYyDEkeG7kyAodHLhu6VjIFgpLiBDw6FjIGNo4bqlbSBtw6B1IMSRZW4gdMawxqFuZyDhu6luZyB24bubaSBnacOhIHRy4buLIHRydW5nIGLDrG5oIExIIOG7nyBt4buXaSBuaMOzbSwgxJHGsOG7o2MgbuG7kWkgduG7m2kgbmhhdSBi4bqxbmcgY8OhYyDEkW/huqFuIHRo4bqzbmcgdGjhu4MgaGnhu4duIGtodXluaCBoxrDhu5tuZyB0xINuZy9naeG6o20gY+G7p2EgZ2nDoSB0cuG7iyBuw6B5Lg0KDQpE4buxYSB2w6BvIGjDrG5oIOG6o25oIG7DoHksIHRhIGPDsyB0aOG7gyByw7p0IHJhIMSRxrDhu6NjIGhhaSB0aMO0bmcgdGluOiB0aOG7qSBuaOG6pXQsIGPDsyBz4buxIGtow6FjIGJp4buHdCByw7UgbsOpdCwga2jDtG5nIGNo4buJIHbhu4EgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBuaMawbmcgY8OybiB24buBIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkgY+G7p2EgTEgsIHRow60gZOG7pSDEkeG7mSBwaMOibiB0w6FuLCDEkeG7mSBs4buHY2guLi4gZ2nhu69hIDUgbmjDs20uIFRo4bupIGhhaSBsw6Ag4bufIG3hu5dpIHBow6JuIG5ow7NtLCBMSCBjaOG6r2MgY2jhuq9uIGtow7RuZyB0aOG7gyBwaMO5IGjhu6NwIHbhu5tpIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIGNodeG6qW4gKEdhdXNzaWFuKS4gUGjDom4gcGjhu5FpIGPhu6dhIExIIGtow7RuZyDEkeG7kWkgeOG7qW5nLCB0aOG6rW0gY2jDrSBjw7MgxJHhu5kgbOG7h2NoIHLhuqV0IGNhbyDhu58gY8OhYyBuaMOzbSBHblJoX2FudCwgTWlsZCB2w6AgUFBPUy4NCg0KVGEgY8OzIHRo4buDIGzDoG0gdMawxqFuZyB04buxIMSR4buDIHThuqFvIHJhIGJp4buDdSDEkeG7lSBib3hwbG90LCBjaOG7iSBj4bqnbiB0aGF5IGjDoG0gZ2VvbV9kZW5zaXR5X3JpZGdlcyBi4bqxbmcgaMOgbSBnZW9tX2JveHBsb3QuIEzGsHUgw70gcuG6sW5nIHbhu5tpIGThuqFuZyBiaeG7g3UgxJHhu5MgYm94cGxvdCBt4bq3YyDEkeG7i25oIHRow6wgdHLhu6VjIHggY2jhu4kgUHJvdG9jb2wsIGPDsm4gdHLhu6VjIHkgY2hvIExILCB0YSB24bqrbiBkw7luZyBow6BtICBjb29yZF9mbGlwKCkgxJHhu4MgeG9heSB0cuG7pWMsIHRyw6xuaCBiw6B5IHRoZW8gcGjGsMahbmcgbmdhbmcsIHbDrCBraOG6oyBuxINuZyBzbyBzw6FuaCBi4bqxbmcgdGjhu4sgZ2nDoWMgc+G6vSB04buRdCBoxqFuIHRoZW8gY2hp4buBdSBuZ2FuZyBzbyB24bubaSBjaGnhu4F1IGThu41jLg0KDQpgYGB7cn0NCmdncGxvdCgpKw0KICBnZW9tX2JveHBsb3QoZGF0YSA9IGRmLA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUHJvdG9jb2wsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gTEgsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQcm90b2NvbCksDQogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gbWVkX2RmLA0KICAgICAgICAgICAgYWVzKHggPSBQcm90b2NvbCwgeSA9IExIKSwNCiAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJywNCiAgICAgICAgICAgIHNoYXBlID0gMTgsDQogICAgICAgICAgICBzaXplID0gNSwNCiAgICAgICAgICAgIGFscGhhID0gMC45KSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuNSw0MCksIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsNDAsYnkgPSA1KSkrDQogIGxhYnMoeSA9ICdMSCBsZXZlbCAobUlVL21MKScsIHggPSAnUHJvdG9jb2xzJykrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoMTApDQpgYGANCg0KDQpCaeG7g3UgxJHhu5MgYm94cGxvdCB0csOsbmggYsOgeSBjw6FjIHRy4buLIHPhu5EgdGjhu5FuZyBrw6ogbcO0IHThuqMgxJHhuqFpIGRp4buHbiBn4buTbSB0cnVuZyB24buLLCB04bupIHBow6JuIHbhu4sgKHBow6JuIHbhu4sgdGjhu6kgMjUsIDUwLCA3NSksIGPDoWMgY2jhuqVtIHRyw7JuIG5o4buPIGzDoCBuaOG7r25nIGdpw6EgdHLhu4sgdsaw4bujdCByYSBraOG7j2kga2hv4bqjbmcgdOG7qSBwaMOibiB24buLLiBDaOG6pW0gbcOgdSDEkWVuIGzhu5tuIG5o4bqldCBsw6AgduG7iyB0csOtIGPhu6dhIHRydW5nIGLDrG5oLiBUdXkga2jDtG5nIG3DtCB04bqjIGjDrG5oIOG6o25oIGPhu6dhIHRvw6BuIHRo4buDIG3huq10IMSR4buZIHBow6JuIHBo4buRaSwgYmnhu4N1IMSR4buTIEJveHBsb3QgduG7q2EgxJHhu6cgxJHhu4MgY2hvIHRhIHRo4bqleSBjw7luZyBuaOG7r25nIHRow7RuZyB0aW4gduG7gSBz4buxIHTGsMahbmcgcGjhuqNuIHLDtSBuw6l0IGdp4buvYSA1IHBow6JuIG5ow7NtLCB0YSBjxaluZyBj4bqjbSBuaOG6rW4gxJHGsOG7o2MgTEgga2jDtG5nIGPDsyBwaMOibiBwaOG7kWkgY2h14bqpbiAodHJ1bmcgduG7iyBraMO0bmcgdHLDuW5nIHbhu5tpIHRydW5nIGLDrG5oLCAyIMSR4bqndSBib3hwbG90IGtow7RuZyBjw6JuIHjhu6luZywgbmjhu69uZyBnacOhIHRy4buLIGNhbyBs4buHY2ggaOG6s24gduG7gSBjaGnhu4F1IGTGsMahbmcgY+G7p2EgdGhhbmcgxJFvIExILi4uKS4NCg0KIyBCxrDhu5tjIDI6IEJp4buHbiBsdeG6rW4gduG7gSB0w61uaCBwaMO5IGjhu6NwIGPhu6dhIHBow6JuIHBo4buRaSBHYW1tYQ0KDQpN4bulYyB0acOqdSBj4bunYSBjw7RuZyDEkW/huqFuIG7DoHkgbmjhurFtIGNo4bupbmcgbWluaCBy4bqxbmcgxJHhu4MgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgTEggdGjDrCBwaMOibiBwaOG7kWkgR2FtbWEgcGjDuSBo4bujcCBoxqFuIHNvIHbhu5tpIHBow6JuIHBo4buRaSBHYXVzc2lhbi4gDQoNClbhu4EgbeG6t3QgbMO9IHRodXnhur90LCB0YSBob8OgbiB0b8OgbiBjw7MgdGjhu4MgY2jhu41uIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIEdhbW1hIGNobyBiaeG6v24gTEggdsOsIG5o4buvbmcgbMO9IGRvIHNhdTogVGhhbmcgxJFvIExIIGPDsyBnacOhIHRy4buLID4wLCBwaMOibiBwaOG7kWkgY+G7p2EgTEggY8OzIGjDrG5oIOG6o25oIGLhuqV0IMSR4buRaSB44bupbmcgdsOgIGzhu4djaCBkxrDGoW5nLiANCg0KVHV5IG5oacOqbiwgdGEgY8OzIHRo4buDIGThu7FhIHbDoG8gY2jhu6luZyBj4bupIHjDoWMgdGjhu7FjIGjGoW4gYuG6sW5nIHF1eSB0csOsbmggc2F1Og0KDQoxKSDEkOG6p3UgdGnDqm4sIHRhIGTDuW5nIGjDoG0gZml0ZmlzdCDEkeG7gyBs4bqnbiBsxrDhu6N0IGto4bubcCBk4buvIGxp4buHdSBMSCB24bubaSAyIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIEdhdXNzaWFuIHbDoCBHYW1tYSwgDQoNCjIpIHNhdSDEkcOzIMSR4buRaSBjaGnhur91IGjDrG5oIOG6o25oIMSR4buTIHRo4buLIGjDoG0gUERGIHbDoCBDREYgc28gduG7m2kgZOG7ryBsaeG7h3UgdGjhu7FjIG5naGnhu4dtLCBi4bqxbmcgMiBow6BtIGRlbnNjb21wIHbDoCBjZGZjb21wOg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmZpdF9nYW0gPSBmaXRkaXN0KGRmJExILCAnZ2FtbWEnLCBvcHRpbS5tZXRob2QgPSAnQkZHUycpDQpmaXRfZ2F1c3MgPSBmaXRkaXN0KGRmJExILCAnbm9ybScsIG9wdGltLm1ldGhvZCA9ICdCRkdTJykNCg0KZGVuc2NvbXAobGlzdChmaXRfZ2F1c3MsIGZpdF9nYW0pLCANCiAgICAgICAgIGxlZ2VuZHRleHQgPSBjKCJHYXVzc2lhbiIsICJHYW1tYSIpLA0KICAgICAgICAgcGxvdHN0eWxlID0gJ2dncGxvdCcsDQogICAgICAgICBmaXRjb2wgPSBjKCcjMDNhOWZjJywnI2ZjMDM2ZicpLA0KICAgICAgICAgZGVtcGNvbCA9IGMoJ2dyZXknKSwNCiAgICAgICAgIGZpdGx0eSA9IDEsDQogICAgICAgICBmaXRsd2QgPSAxKSsNCiAgbGFicyh4ID0gJ0xIIG9uIHRyaWdnZXIgZGF5IChtVUkvbUwpJykrIA0KICB0aGVtZV9idygpDQpgYGANCg0KQ2jDuiB0aMOtY2g6IEJp4buDdSDEkeG7kyB0csOqbiBjaG8gcGjDqXAgxJHhu5FpIGNoaeG6v3UgxJHhu5MgdGjhu4sgaMOgbSBQREYgKG3huq10IMSR4buZIHjDoWMgc3XhuqV0KSBj4bunYSAyIHBow6JuIHBo4buRaSBHYXVzc2lhbiAobcOgdSB4YW5oKSwgR2FtbWEgKG3DoHUgxJHhu48pIHbhu5tpIGjDrG5oIOG6o25oIGJp4buDdSDEkeG7kyB04bqnbiBzdeG6pXQgdHLDqm4gdGjhu7FjIG5naGnhu4dtIChtw6B1IHjDoW0pLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmNkZmNvbXAobGlzdChmaXRfZ2F1c3MsIGZpdF9nYW0pLCANCiAgICAgICAgbGVnZW5kdGV4dCA9IGMoIkdhdXNzaWFuIiwgIkdhbW1hIiksDQogICAgICAgIHBsb3RzdHlsZSA9ICdnZ3Bsb3QnLA0KICAgICAgICBmaXRjb2wgPSBjKCcjMDNhOWZjJywnI2ZjMDM2ZicpLA0KICAgICAgICBmaXRsdHkgPSAxLA0KICAgICAgICBkYXRhY29sID0gJ2dyZXknKSsNCiAgbGFicyh4ID0gJ0xIIG9uIHRyaWdnZXIgZGF5IChtVUkvbUwpJykrIA0KICB0aGVtZV9idygpDQpgYGANCg0KQ2jDuiB0aMOtY2g6IEJp4buDdSDEkeG7kyB0csOqbiBjaG8gcGjDqXAgxJHhu5FpIGNoaeG6v3UgxJHhu5MgdGjhu4sgaMOgbSBDREYgKG3huq10IMSR4buZIHjDoWMgc3XhuqV0IHTDrWNoIGzFqXkpIGPhu6dhIDIgcGjDom4gcGjhu5FpIEdhdXNzaWFuIChtw6B1IHhhbmgpLCBHYW1tYSAobcOgdSDEkeG7jykgduG7m2kgaMOgbSBDREYgdHLDqm4gdGjhu7FjIG5naGnhu4dtIChtw6B1IHjDoW0pLg0KDQpD4bqjIDIga+G6v3QgcXXhuqMgbsOgeSBjaG8gdGjhuqV5IHBow6JuIHBo4buRaSBHYW1tYSB0aMOtY2ggaOG7o3AgaMahbiBzbyB24bubaSBwaMOibiBwaOG7kWkgR2F1c3NpYW4gxJHhu4MgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgTEgsIHbDrCBow6BtIFBERiB2w6AgQ0RGIGPhu6dhIHBow6JuIHBo4buRaSBHYW1tYSBwaMO5IGjhu6NwIGjGoW4gduG7m2kgZOG7ryBsaeG7h3UgdGjhu7FjIG5naGnhu4dtLg0KDQpM4bujaSDDrWNoIGPhu6dhIHZp4buHYyBjaOG7jW4gcXV5IGx14bqtdCBwaMOibiBwaOG7kWkgxJHDum5nIGNobyBiaeG6v24ga+G6v3QgcXXhuqMgbcO0IGjDrG5oIGjhu5NpIHF1eSwgxJHDsyBsw6AgbsOzIGNobyBwaMOpcCDGsOG7m2MgbMaw4bujbmcgdsOgIHN1eSBsdeG6rW4gdGjhu5FuZyBrw6ogY2jDrW5oIHjDoWMgaMahbiwgcGjDuSBo4bujcCB0aOG7sWMgdOG6vyBoxqFuLiANCg0KIyBCxrDhu5tjIDM6IEThu7FuZyBtw7QgaMOsbmggaOG7k2kgcXV5IHbhu5tpIHBow6JuIHBo4buRaSBHYW1tYQ0KDQpOaMawIMSRw6MgdHLDrG5oIGLDoHkgdHJvbmcga+G6vyBob+G6oWNoIHBow6JuIHTDrWNoLCBjw7RuZyBj4bulIHRo4buRbmcga8OqIGNow61uaCBtw6AgdGEgc+G7rSBk4bulbmcgbMOgIG3hu5l0IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgKEdMTSkuIEjhu4cgdGjhu5FuZyBtw7QgaMOsbmggR0xNIGRvIDIgbmjDoCB0aOG7kW5nIGvDqiBKb2huIE5lbGRlciB2w6AgUm9iZXJ0IFdlZGRlcmJ1cm4gdGhp4bq/dCBs4bqtcCB2w6BvIG7Eg20gMTk3MiBjaG8gcGjDqXAgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgY+G7p2EgYmnhur9uIGvhur90IHF14bqjIHRoZW8gbmhp4buBdSBxdXkgbHXhuq10IHBow6JuIHBo4buRaSDEkWEgZOG6oW5nLg0KDQpN4buZdCBjw6FjaCB04buVbmcgcXXDoXQsIG3DtCBow6xuaCBHTE0gxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggKHRoYW0gc+G7kSAkXG11JCkgY+G7p2EgYmnhur9uIG5n4bqrdSBuaGnDqm4gWSwgxJFp4buBdSBraeG7h24gaMOzYSB0aGVvIG3hu5l0IHThuq1wIGJp4bq/biBYIHRow7RuZyBxdWEgbeG7mXQgaMOgbSBsacOqbiBr4bq/dCAkZyQgdsOgIG3hu5l0IG3DtCBow6xuaCBo4buTaSB0dXnhur9uIHTDrW5oICRYXGJldGEkLg0KDQokJEUoWXxYKSA9IFxtdV97WX0gXHNpbSBnXnstMX0oWFxiZXRhKSQkDQpUcm9uZyBiw6BpIHRvw6FuIGhp4buHbiB0aOG7nWksIGJp4bq/biBr4bq/dCBxdeG6oyBZIGzDoCBnacOhIHRy4buLIGPhu6dhIExIIG5nw6B5IHRyaWdnZXIsIMSRxrDhu6NjIGdp4bqjIMSR4buLbmggbMOgIG3hu5l0IGJp4bq/biBuZ+G6q3Ugbmhpw6puIHRoZW8gcXV5IGx14bqtdCBwaMOibiBwaOG7kWkgR2FtbWEgduG7m2kgMiB0aGFtIHPhu5EgJFxtdSQgdsOgICRcc2lnbWEkDQoNCkjDoG0gbeG6rXQgxJHhu5kgcGjDom4gcGjhu5FpIChQREYpIGPhu6dhIHBow6JuIHBo4buRaSBHYW1tYSDEkcaw4bujYyB4w6FjIMSR4buLbmggbmjGsCBzYXU6DQoNCiQkZih5fFxtdSAsXHNpZ21hKT1cZnJhY3sxfXtcbGVmdCAoIFxzaWdtYSBeezJ9IFxtdSBccmlnaHQgKV57MS9cc2lnbWFeezJ9fX1cZnJhY3t5XntcZnJhY3sxfXtcc2lnbWEgXnsyfX0tMX1lXnsteS8oXHNpZ21hIF57Mn1cbXUgKX19e1xHYW1tYSBcbGVmdCAoIDEvXHNpZ21hXnsyfSBccmlnaHQgKX0kJA0KDQokXG11JCBjw7Mgw70gbmdoxKlhIG5oxrAgdHLhu41uZyB0w6JtIChjZW50cmFsIHRlbmRlbmN5KSBoYXkgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBiaeG6v24ga+G6v3QgcXXhuqMgY+G6p24gxrDhu5tjIGzGsOG7o25nLCBjw7JuIGdpw6EgdHLhu4sgY+G7p2EgJFxzaWdtYSQgdMawxqFuZyDhu6luZyB24bubaSBt4bupYyDEkeG7mSBwaMOibiB0w6FuIHbDoCB4w6FjIMSR4buLbmggaMOsbmgg4bqjbmggY+G7p2EgaMOgbSBt4bqtdCDEkeG7mSBwaMOibiBwaOG7kWkuIE3hu5l0IGtoaSBjw7MgbXUgdsOgIHNpZ21hLCBjw7MgdGjhu4MgxrDhu5tjIHTDrW5oIMSR4buZIGzhu4djaCBjaHXhuqluICh2w6AgcGjGsMahbmcgc2FpKSBj4bunYSBMSCB0aGVvIGPDtG5nIHRo4bupYzoNCg0KJCRTRCA9IFxzaWdtYSAqIFxtdSQkDQoNClBow6JuIHTDrWNoIGjhu5NpIHF1eSBHYW1tYSBkw7luZyB0aMawIHZp4buHbiBnYW1sc3MuIMSQ4bqndSB0acOqbiwgdGEga2jhu5twIG3hu5l0IG3DtCBow6xuaCBo4buTaSBxdXkgR2FtbWEgY2jhu4kgY2jhu6lhIEludGVyY2VwdC4gTcO0IGjDrG5oIG7DoHkgdMawxqFuZyDhu6luZyB24bubaSB2aeG7h2MgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggY+G7p2EgTEggY2hvIHRvw6BuIGLhu5kgbeG6q3Uga2jhuqNvIHPDoXQsIGtow7RuZyBwaMOibiBiaeG7h3QgbmjDs20gcGjDoWMgxJHhu5MgbsOgby4NCg0KVGEgZMO5bmcgaMOgbSBnYW1sc3MsIHbhu5tpIGPDtG5nIHRo4bupYyBmb3JtdWxhID0gTEggfiAxLCB2w6AgZmFtaWx5ID0gR0EgDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCiMgTcO0IGjDrG5oIGNo4buJIGPDsyBpbnRlcmNlcHQNCg0KbTAgPSBnYW1sc3MoZm9ybXVsYSA9IExIIH4gMSwNCiAgICAgICAgICAgICBkYXRhPWRmLA0KICAgICAgICAgICAgIGZhbWlseSA9IEdBLA0KICAgICAgICAgICAgIHRyYWNlPUYsDQogICAgICAgICAgICAgcGFyYWxsZWw9Im11bHRpY29yZSIsDQogICAgICAgICAgICAgbmNwdXMgPSBuQykNCmBgYA0KDQpUYSBz4bq9IGTDuW5nIG3DtCBow6xuaCBjxqEgYuG6o24gbsOgeSDEkeG7gyBsw6BtIHRoYW0gY2hp4bq/dSB2w6Agc28gc8OhbmggduG7m2kgbcO0IGjDrG5oIGNow61uaCB0cm9uZyBwaMOibiB0w61jaCwgbmjhurFtIGNo4bupbmcgbWluaCBy4bqxbmcgeeG6v3UgdOG7kSBjaGlhIG5ow7NtIHRoZW8gcGjDoWMgxJHhu5MgdGjhu7FjIHPhu7EgY8OzIOG6o25oIGjGsOG7n25nIMSR4bq/biBnacOhIHRy4buLIHRydW5nIGLDrG5oIExILg0KDQpO4buZaSBkdW5nIG3DtCBow6xuaCBjxqEgYuG6o24gKGNo4buJIGNo4bupYSBJbnRlcmNlcHQsIMaw4bubYyBsxrDhu6NuZyBMSCB0cnVuZyBiw6xuaCBjaG8gdG/DoG4gcXXhuqduIHRo4buDKSBjw7MgbuG7mWkgZHVuZyBuaMawIHNhdToNCg0KYGBge3J9DQpzdW1tYXJ5KG0wKQ0KYGBgDQpnYW1sc3MgZOG7sW5nIDIgbcO0IGjDrG5oIGjhu5NpIHF1eSByacOqbmcgY2hvICRcbXUkIHbDoCAkXHNpZ21hJCwgY+G6oyAyIG3DtCBow6xuaCDEkeG7gXUgc+G7rSBk4bulbmcgaMOgbSBsacOqbiBr4bq/dCBsw6AgbG9nYXJpdCwgdsOsIHbhuq15IHRoYW5nIMSRbyBj4bunYSBjw6FjIGjhu4cgc+G7kSBo4buTaSBxdXkgaGnhu4duIHRo4budaSBsw6AgbG9nKExIKSwgxJHhu4MgxJHGsGEgduG7gSB0aGFuZyDEkW8gbmd1ecOqbiB0aOG7p3kgKG1VSS9tTCkgdGEgcGjhuqNpIGTDuW5nIGjDoG0gZXhwb25lbnRpYWwuDQoNCk3DtCBow6xuaCB0aOG7qSBuaOG6pXQgY2hvIGJp4bq/dCBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIExIIGNobyB0b8OgbiBxdeG6p24gdGjhu4MgJFxtdSA9IGV4cCgxLjQ5MDQyKSA9IDQuNDM4OTU5JCANCg0KVGEgY8OzIHRo4buDIMSR4buRaSBjaGnhur91IHbhu5tpIGvhur90IHF14bqjIMaw4bubYyBsxrDhu6NuZyB04burIGThu68gbGnhu4d1IHRo4buxYyBuZ2hp4buHbSwgdsOgIHRo4bqleSBy4bqxbmcgSW50ZXJjZXB0IGPhu6dhIG3DtCBow6xuaCBtMCBjxqEgYuG6o24gcuG6pXQgcGjDuSBo4bujcCB24bubaSBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIExIDQoNCmBgYHtyfQ0KbWVhbihkZiRMSCkNCmBgYA0KDQoyKSBUaeG6v3AgdGhlbywgdGEgZOG7sW5nIG3hu5l0IG3DtCBow6xuaCBraMOhYyB24bubaSAxIGJp4bq/biDEkeG7mWMgbOG6rXAgbMOgIHBow6JuIG5ow7NtIFByb3RvY29sLiBNw7QgaMOsbmggbsOgeSBjaG8gcGjDqXAgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTEgg4bufIDUgcGjDom4gbmjDs20gUHJvdG9jb2wga2jDoWMgbmhhdSwgaGF5IG7Ds2kgY8OhY2gga2jDoWMsIGto4bqjbyBzw6F0IGxpw6puIGjhu4cgZ2nhu69hIGJp4bq/biBwcm90b2NvbCB2w6AgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBMSCAoaGF5IGhp4buHdSDhu6luZyBj4bunYSBQcm90b2NvbCDEkeG7kWkgduG7m2kgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBMSCkuDQoNCiQkXG11X3tMSH0gXHNpbSBcYmV0YV8wKkxvbmcgKyBcYmV0YV8xKkduUkhhbnQgKyBcYmV0YV8yKlNob3J0ICsgXGJldGFfMypNaWxkICsgXGJldGFfNCpQUE9TJCQNCg0KTcO0IGjDrG5oIG7DoHkgY8OzIGPDtG5nIHRo4bupYyBjaG8gdGhhbSBz4buRIGNow61uaCAodHJ1bmcgYsOsbmggY+G7p2EgcGjDom4gcGjhu5FpIEdhbW1hKSBsw6AgTEggfiBQcm90b2NvbDsgdsOgIGNobyB0aGFtIHPhu5Egc2lnbWE6IHNpZ21hLmZvcm11bGEgPSB+IFByb3RvY29sLg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQojIE3DtCBow6xuaCBBTk9WQSAxIGJp4bq/bg0KDQptMSA9IGdhbWxzcyhmb3JtdWxhID0gTEggfiBQcm90b2NvbCwNCiAgICAgICAgICAgIHNpZ21hLmZvcm11bGEgPSB+IFByb3RvY29sLA0KICAgICAgICAgICAgZGF0YT1kZiwNCiAgICAgICAgICAgIGZhbWlseSA9IEdBLA0KICAgICAgICAgICAgdHJhY2U9RiwNCiAgICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgICAgbmNwdXMgPSBuQykNCmBgYA0KDQrhu54gxJHDonkgdGEgxrDhu5tjIGzGsOG7o25nIMSR4buTbmcgdGjhu51pIGPhuqMgMiB0aGFtIHPhu5EgY+G7p2EgcGjDom4gcGjhu5FpIEdhbW1hIGzDoCBtdSAodHJ1bmcgYsOsbmgpIHbDoCBzaWdtYSAodGhhbSBz4buRIGtp4buDdSBow6xuaCkgdGhlbyBiaeG6v24gUHJvdG9jb2wuIFbDrCB0YSBraMO0bmcgY2jhu4kgcXVhbiB0w6JtIMSR4bq/biB2aeG7h2MgUHJvdG9jb2wgc+G6vSBsw6BtIHRoYXkgxJHhu5VpIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTEggKHRy4buNbmcgdMOibSBj4bunYSBwaMOibiBwaOG7kWkgR2FtbWEsIHRoYW0gc+G7kSAkXG11JCksIG3DoCBjw7JuIG114buRbiBiaeG6v3QgbGnhu4d1IHBow6JuIGxv4bqhaSBQcm90b2NvbCBjw7MgdMOhYyDEkeG7mW5nIGfDonkgdGhheSDEkeG7lWkgaMOsbmgg4bqjbmggKMSR4buZIHBow6JuIHTDoW4sIMSR4buZIGzhu4djaCkgY+G7p2EgcGjDom4gcGjhu5FpIEdhbW1hICh0aGFtIHPhu5EgJFxzaWdtYSQpIGhheSBraMO0bmcgPw0KDQpO4buZaSBkdW5nIGPhu6dhIG3DtCBow6xuaCBjaMOtbmggKMaw4bubYyBsxrDhu6NuZyBMSCB0aGVvIHBow6FjIMSR4buTKSBjw7MgbuG7mWkgZHVuZyBuaMawIHNhdQ0KDQpgYGB7cn0NCnN1bW1hcnkobTEpDQpgYGANCg0KTcO0IGjDrG5oIG0xIG7DoHkgY2hvIHBow6lwIMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIExIIOG7nyBt4buXaSBwaMOibiBuaMOzbSBQcm90b2NvbC4gTMawdSDDvSBy4bqxbmcgcGjDoWMgxJHhu5MgRm9sX2xvbmcgxJHGsOG7o2MgY2jhu41uIGzDoG0gdGhhbSBjaGnhur91LCB2w6wgduG6rXkgSW50ZXJjZXB0IHRyb25nIG3DtCBow6xuaCB0xrDGoW5nIOG7qW5nIHbhu5tpIHRydW5nIGLDrG5oIGPhu6dhIGxvZyhMSCkgdHJvbmcgcGjDom4gbmjDs20gUHJvdG9jb2wgPSBGb2xfbG9uZy4NCg0KVGEgY8OzIHRo4buDIMSR4buRaSBjaGnhur91IHbhu5tpIGvhur90IHF14bqjIHRo4buRbmcga8OqIG3DtCB04bqjIGJhbiDEkeG6p3U6DQoNCmBgYHtyfQ0KZGYlPiUgDQogIGdyb3VwX2J5KFByb3RvY29sKSU+JQ0KICBzdW1tYXJpemUoJ21lYW4nID0gbWVhbihMSCkpICU+JSBrbml0cjo6a2FibGUoKQ0KYGBgDQoNCmV4cChJbnRlcmNlcHQpIHTGsMahbmcg4bupbmcgduG7m2kgdHJ1bmcgYsOsbmggY+G7p2EgTEggdHJvbmcgcGjDoWMgxJHhu5MgRm9sbGljdWxhciBsb25nIGFjdGluZywgxJHGoW4gduG7iyBtVUkvbUw6ICRleHAoMC4zMTIzNSkgPSAxLjM2NjYzMyQsIHRhIHRo4bqleSBnacOhIHRy4buLIG7DoHkgdMawxqFuZyDEkeG7k25nIHbhu5tpIGvhur90IHF14bqjIG1lYW4gTEgg4bufIG5ow7NtIEZvbF9Mb25nDQoNCkdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTEggbmjDs20gR25SSF9hbnQgxJHGsOG7o2MgdMOtbmggYuG6sW5nIDogZXhwKEludGVyY2VwdCArIFByb3RvY29sR25SSF9hbnQpOiAkZXhwKDAuMzEyMzUgKyAxLjczOTc4KSA9IDcuNzg0NDY0JCANCi4uLg0KDQpD4bupIHRo4bq/LCB0YSBjw7MgdGjhu4MgxrDhu5tjIGzGsOG7o25nIMSRxrDhu6NjIHRydW5nIGLDrG5oIGPhu6dhIGxvZyhMSCkgaG/hurdjIExIIOG7nyBj4bqjIDUgcGjDom4gbmjDs20uDQoNCiMgQsaw4bubYyA0OiBTdXkgZGnhu4VuIHRo4buRbmcga8OqDQoNCkThu7FhIHbDoG8gMiBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgbsOgeSwgdGEgY8OzIHRo4buDIHRo4buxYyBoaeG7h24gbeG7mXQgc+G7kSBzdXkgZGnhu4VuIHRo4buRbmcga8OqIG5oxrAgc2F1Og0KDQrEkOG6p3UgdGnDqm4sIGtoaSBzbyBzw6FuaCBtw7QgaMOsbmggY2jhu4kgY2jhu6lhIGludGVyY2VwdCB2w6AgbcO0IGjDrG5oIGPDsyB0aMOqbSBiaeG6v24gUHJvdG9jb2wgYuG6sW5nIG3hu5l0IGtp4buDbSDEkeG7i25oIExpa2VsaWhvb2QgcmF0aW8sIHRhIGPDsyB0aOG7gyBiaeG6v3QgbGnhu4d1IHBow6JuIG5ow7NtIHBow6FjIMSR4buTIHRo4buxYyBz4buxIGPDsyBoaeG7h3Ug4bupbmcgw70gbmdoxKlhIMSR4buRaSB24bubaSBMSCBoYXkga2jDtG5nID8gKGPDonUgaOG7j2kgbsOgeSB0xrDGoW5nIHThu7EgduG7m2kga2nhu4NtIMSR4buLbmggRiB0aGVvIEZpc2hlciB0cm9uZyBwaMOibiB0w61jaCBwaMawxqFuZyBzYWkgY+G7lSDEkWnhu4NuKQ0KDQpgYGB7cn0NCkxSLnRlc3QobTAsbTEpDQpgYGANCg0KS+G6v3QgcXXhuqMgY2hvIHJhIG3hu5l0IGdpw6EgdHLhu4sgcCBj4buxYyBrw6wgdGjhuqVwLCBuaMawIHbhuq15IHRhIGPDsyB0aOG7gyBraOG6s25nIMSR4buLbmggcuG6sW5nIHBow6JuIG5ow7NtIHBow6FjIMSR4buTIHRo4buxYyBz4buxIGPDsyB0w6FjIMSR4buZbmcgbMOgbSB0aGF5IMSR4buVaSBMSC4NCg0KTsOzaSBjw6FjaCBraMOhYywgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IMO9IG5naMSpYSB24buBIGdpw6EgdHLhu4sgTEggZ2nhu69hIGPDoWMgbmjDs20gcGjDoWMgxJHhu5Mga2jDoWMgbmhhdS4gVHV5IG5oacOqbiB0YSBjaMawYSB0aOG7gyBiaeG6v3QgcsO1IHPhu7Ega2jDoWMgYmnhu4d0IG7DoHkgbMOgIOG7nyBuaMOzbSBuw6BvIHNvIHbhu5tpIG5ow7NtIG7DoG8uIA0KDQpO4buZaSBkdW5nIGjhu4cgc+G7kSBo4buTaSBxdXkgdHJvbmcgbcO0IGjDrG5oIGNobyBwaMOpcCB0YSBraeG7g20gxJHhu4tuaCB24buBIHPhu7Ega2jDoWMgYmnhu4d0IGPhu6dhIExIIHRydW5nIGLDrG5oIOG7nyBwaMOhYyDEkeG7kyBQUE9TLCBHblJILCBMdXRlYWwgc2hvcnQgYWN0aW5nIHbDoCBNaWxkIChMZXRyb3pvbGUpIHNvIHbhu5tpIG5ow7NtIHRoYW0gY2hp4bq/dSBsw6AgRm9sbGljdWxhciBsb25nIGFjdGluZy4gDQoNCkThu7FhIHbDoG8ga+G6v3QgcXXhuqMgdGjDtCBj4bunYSBtw7QgaMOsbmgsIGPDsyB0aOG7gyB0aOG6pXkgdOG6pXQgY+G6oyBo4buHIHPhu5EgaOG7k2kgcXV5IMSR4buBdSA+MCB2w6AgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiwgY2hvIHRo4bqleSBnacOhIHRy4buLIHRydW5nIGLDrG5oIExIIOG7nyA0IHBow6FjIMSR4buTIGPDsm4gbOG6oWkgxJHhu4F1IGNhbyBoxqFuIHNvIHbhu5tpIHBow6FjIMSR4buTIHRoYW0gY2hp4bq/dSAoTG9uZ2FjdGluZykuDQoNClR1eSBuaGnDqm4sIHRhIHPhur0ga2jDtG5nIHN1eSBkaeG7hW4gdHLhu7FjIHRp4bq/cCBk4buxYSB2w6BvIGjhu4cgc+G7kSBo4buTaSBxdXksIG3DoCB0aeG6v24gaMOgbmggbeG7mXQgcGjDom4gdMOtY2ggaOG6rXUga2nhu4NtIHbhu5tpIG7hu5lpIGR1bmcgc28gc8OhbmggYuG6r3QgY+G6t3AgdHXhuqduIHThu7EgZ2nhu69hIDUgbG/huqFpIHBow6FjIMSR4buTLiBUcm9uZyBraGkgbMOgbSB0aOG7kW5nIGvDqiBtw7QgdOG6oywgdGEgY8OzIGPhuqNtIG5o4bqtbiB24buBIGtodXluaCBoxrDhu5tuZyB0xINuZyBk4bqnbiBj4bunYSBnacOhIHRy4buLIExIIHRydW5nIGLDrG5oIHRoZW8gdGjhu6kgdOG7sTogRm9sX2xvbmcgPCBTaG9ydCA8ICBQUE9TIDwgR25SSF9hbnQgPCBNaWxkDQoNCuG7niDEkcOieSB0YSBz4bq9IGtp4buDbSB0cmEgbOG6oWkga+G6v3QgcXXhuqMgbsOgeS4NCg0KxJDhu4MgdGnhur9uIGjDoG5oIHBow6JuIHTDrWNoIGjhuq11IGtp4buDbSBuw6B5LCB0YSBkw7luZyBow6BtIGF2Z19jb21wYXJpc29ucyBj4bunYSB0aMawIHZp4buHbiBtYXJnaW5hbHNlZmZlY3QgduG7m2kgdMO5eSBjaOG7iW5oIHZhcmlhYmxlcyA9IGxpc3QoUHJvdG9jb2wgPSAncGFpcndpc2UnKSwgc2F1IMSRw7MgaGnhu4d1IGNo4buJbmggZ2nDoSB0cuG7iyBwIGLhurFuZyBow6BtIHAuYWRqdXN0IHRyb25nIFIsIHbhu5tpIHBoxrDGoW5nIHBow6FwIGhp4buHdSBjaOG7iW5oIEJvbmZlcnJvbmkuDQoNCkvhur90IHF14bqjIGPhu6dhIGjDoG0gbsOgeSBsw6AgbeG7mXQgYuG6o25nIG5oxrAgc2F1Og0KDQpgYGB7cn0NCnB3X2NvbXAgPSBhdmdfY29tcGFyaXNvbnMobTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHdoYXQgPSAnbXUnLA0KICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9IGxpc3QoUHJvdG9jb2wgPSAncGFpcndpc2UnKSkNCg0KcHdfY29tcCRhZGpfcCA9IHAuYWRqdXN0KHB3X2NvbXAkcC52YWx1ZSwgbWV0aG9kID0gJ2JvbmZlcnJvbmknKQ0KDQpwd19jb21wICU+JSBkcGx5cjo6c2VsZWN0KC1jKDEsMiw2LDcpKSAlPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNCkPhu5l0IGNvbnRyYXN0IHRyw6xuaCBiw6B5IGPDoWMgY+G6t3Agc28gc8OhbmggbcOgIHRhIGPhuqduIGtp4buDbSBuZ2hp4buHbSwgdGjDrSBk4bulIEduUkhfYW50IC0gRm9sX2xvbmc7DQoNCkPhu5l0IGVzdGltYXRlIHRyw6xuaCBiw6B5IGdpw6EgdHLhu4sga2jDoWMgYmnhu4d0IHRydW5nIGLDrG5oIHbhu4EgTEggZ2nhu69hIDIgbmjDs20gcGjDoWMgxJHhu5MsIHRow60gZOG7pSBHblJIYW50IGNhbyBoxqFuIExvbmcgYWN0aW5nIDYuNDE3OCBtVUkvbUwuDQoNCjIgY+G7mXQgY29uZi5sb3cJY29uZi5oaWdoCXRyw6xuaCBiw6B5IG5nxrDhu6FuZyBkxrDhu5tpIHbDoCB0csOqbiBraG/huqNuZyB0aW4gY+G6rXkgOTUlIGPhu6dhIGtow6FjIGJp4buHdCwgS1RDOTUlIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6oga2hpIGtow7RuZyBjaOG7qWEgZ2nDoSB0cuG7iyAwLg0KDQpD4buZdCBhZGpfcCB0csOsbmggYsOgeSBnacOhIHRy4buLIHAgc2F1IGhp4buHdSBjaOG7iW5oIEJvbmZlcnJvbmksIA0KDQpL4bq/dCBxdeG6oyBjaG8gdGjhuqV5IHRvw6BuIGLhu5kgMTAgZ2nhuqMgdGh1eeG6v3QgxJHhu4F1IGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouIE3hu5l0IGPDoWNoIHThu5VuZyBxdcOhdCwgZ2nDoSB0cuG7iyBMSCB0cnVuZyBiw6xuaCB0xINuZyBk4bqnbiB0aGVvIHRo4bupIHThu7EgNSBwaMOhYyDEkeG7kzoNCg0KRm9sX2xvbmcgPCBTaG9ydCA8IFBQT1MgPCBHblJIX2FudCA8IE1pbGQNCg0KTmjGsCB24bqteSBwaMOhYyDEkeG7kyBNaWxkIHN0aW11bGF0aW9uIHbhu5tpIExldHJvem9sZSBjw7MgTEggdHJ1bmcgYsOsbmggbmfDoHkgdHJpZ2dlciBjYW8gbmjhuqV0LCBwaMOhYyDEkeG7kyBGb2xsaWN1bGFyIGxvbmcgYWN0aW5nIGPDsyBMSCB0cnVuZyBiw6xuaCB0aOG6pXAgbmjhuqV0LCBwaMOhYyDEkeG7kyBQUE9TIOG7nyB24buLIHRyw60gdHJ1bmcgZ2lhbiwgduG7m2kgTEggY2FvIGjGoW4gc28gduG7m2kgMiBwaMOhYyDEkeG7kyBMb25nIHbDoCBTaG9ydC4NCg0KQ3Xhu5FpIGPDuW5nLCBr4bq/dCBxdeG6oyBj4bunYSBtw7QgaMOsbmggaOG7k2kgcXV5IGPDsyB0aOG7gyB0csOsbmggYsOgeSB0cuG7sWMgcXVhbiBuaMawIHNhdToNCg0KYGBge3J9DQptMSU+JWF1Z21lbnQoKSU+JW11dGF0ZShVTD0gLmZpdHRlZCArLnNlLmZpdCoxLjk2LA0KICAgICAgICAgICAgICAgICAgICAgTEw9LmZpdHRlZC0uc2UuZml0KjEuOTYpJT4lDQogIGdyb3VwX2J5KFByb3RvY29sKSU+JQ0KICBzdW1tYXJpemVfYXQoJy5maXR0ZWQnLCBtZWRpYW4pIC0+IG0xX3N1bQ0KDQphdWdtZW50KG0xKSU+JW11dGF0ZShVTD0gLmZpdHRlZCArLnNlLmZpdCoxLjk2LA0KICAgICAgICAgICAgICAgICAgICAgTEw9LmZpdHRlZC0uc2UuZml0KjEuOTYpJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2ppdHRlcihhZXMoeSA9IFByb3RvY29sLCANCiAgICAgICAgICAgICAgICAgIHggPSBMSCwNCiAgICAgICAgICAgICAgICAgIGNvbCA9IFByb3RvY29sKSwNCiAgICAgICAgICAgICAgc2NhbGUgPSAwLjgsDQogICAgICAgICAgICAgIGFscGhhID0gMC4zLA0KICAgICAgICAgICAgICB3aWR0aCA9IDAuMDEsDQogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWVzKHkgPSBQcm90b2NvbCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBMSCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFByb3RvY29sKSwNCiAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDAuOCwNCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywNCiAgICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5PVByb3RvY29sLCANCiAgICAgICAgICAgICAgICAgICAgeG1pbj1leHAoTEwpLCANCiAgICAgICAgICAgICAgICAgICAgeG1heD1leHAoVUwpKSwNCiAgICAgICAgICAgICAgICB3aWR0aD0wLjEsDQogICAgICAgICAgICAgICAgc2l6ZT0xKSArIA0KICBnZW9tX3BhdGgoZGF0YSA9IG0xX3N1bSwNCiAgICAgICAgICAgIGFlcyh5PVByb3RvY29sLCANCiAgICAgICAgICAgICAgICB4PWV4cCguZml0dGVkKSwNCiAgICAgICAgICAgICAgICBncm91cCA9IDEpKSsNCiAgZ2VvbV9wb2ludChhZXMoeT1Qcm90b2NvbCwgDQogICAgICAgICAgICAgICAgIHg9ZXhwKC5maXR0ZWQpKSwgDQogICAgICAgICAgICAgc2l6ZT0zKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDQwLGJ5ID0gNSkpKw0KICBsYWJzKHggPSAnTEggbGV2ZWwgb24gdHJpZ2dlciBkYXkgKG1JVS9tTCknLCB5ID0gJ1Byb3RvY29scycpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lX2J3KCkgIA0KYGBgDQoNCiMgS+G6v3QgbHXhuq1uDQoNClRhIHRo4bqleSBtw7QgaMOsbmggR0xNIHbhu5tpIHBow6JuIHBo4buRaSBHYW1tYSBsw6AgbeG7mXQgZ2nhuqNpIHBow6FwIHBo4buVIHF1w6F0IHbDoCBoaeG7h3UgcXXhuqMgY2hvIGjhuqd1IGjhur90IG5o4buvbmcgdMOsbmggaHXhu5FuZyBuZ2hpw6puIGPhu6l1IHkgaOG7jWMgbMOibSBzw6BuZy4gTcO0IGjDrG5oIEdMTSBHYW1tYSBjw7MgdGjhu4MgdGhheSB0aOG6vyBob8OgbiB0b8OgbiBwaMOibiB0w61jaCBwaMawxqFuZyBzYWkgKEFOT1ZBKSBj4buVIMSRaeG7g24gdsOgIGPhuqMgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIHRow7RuZyB0aMaw4budbmcu