
Giới thiệu
Những nghiên cứu về quy trình thụ tinh ống nghiệm đặt ra yêu cầu khảo
sát nhiều loại kết cục lâm sàng với bản chất khác nhau. Mặt khác, có
tính chất kế thừa trong chuỗi kết quả của quy trình. Thành phẩm thu được
của công đoạn sau chính là hệ quả từ việc tiến hành một loạt phép thử
(thí dụ thụ tinh, chuyển phôi…) trên tập hợp kết quả công đoạn trước.
Thí dụ, số lượng phôi là hệ quả từ tỷ lệ thụ tinh thành công trên tập
hợp nhiều noãn. Do đó, cùng một thực thể kết cục nhưng có thể được diễn
đạt theo nhiều cách; số lượng phôi, tỷ lệ thụ tinh trên tổng số noãn,
xác suất thụ tinh thành công cho mỗi noãn…
Hầu hết những nghiên cứu hiện nay chỉ mới dừng lại ở sự so sánh và
kiểm định bằng những phép kiểm thống kê thô sơ như Mann-Whitney, Student
t test, cho mọi kết quả là con số, không phân biệt bản chất của chúng la
số đếm, số liên tục hay tỷ lệ.
Trong chương này, chúng tôi giới thiệu một phương pháp phân tích hợp
lý và chính xác hơn cho kết cục là tỷ lệ, áp dụng lý thuyết xác suất cho
biến rời rạc và mô hình hồi quy tuyến tính tổng quát với phân phối nhị
thức (Binomial).
Tình huống minh họa như sau: Một bác sĩ muốn khảo sát hiệu quả tạo
phôi (blastocytes) của kỹ thuật kích thích trưởng thành noãn kép (Dual
trigger), so với 2 kỹ thuật tham chiếu dùng agonist và hCG đơn giản.
Kế hoạch phân tích
Khả năng tạo blastocyte khi dùng kỹ thuật Dual trigger so với 2
phương pháp tham chiếu (đồng vận hoặc hCG đơn) sẽ được ước lượng bằng
marginal Odds-ratio (OR) và risk-ratio (RR), có hiệu chỉnh cho Tuổi và
AFC, dựa vào một mô hình hồi quy tuyến tính tổng quát (GLM) với phân
phối nhị thức (Binomial).
Suy diễn thống kê dựa vào phủ nhận giả thuyết vô hiệu (OR và RR = 1)
ở ngưỡng ý nghĩa p = 0.05.
Công cụ cần thiết cho
quy trình
Thư viện dplyr trong hệ sinh thái tidyverse để thao tác dữ liệu
và thống kê mô tả
Thư viện ggplot2, ggrides, tidybayes và ggdist để vẽ một số biểu
đồ thống kê;
Thư viện gamlss để dựng mô hình GLM với phân phối
Binomial
Thư viện marginaleffects để ước tính OR, RR và suy diễn thống kê
từ kết quả mô hình.
library(tidyverse)
library(ggridges)
library(gamlss)
library(marginaleffects)
library(tidybayes)
library(ggdist)
Đầu tiên, ta tải dữ liệu từ file Dual_trigger.csv vào dataframe df
bằng hàm read.csv, sau đó áp dụng hàm factor để chuyển dạng biến Trigger
thành số thứ tự (1 = a (agonist), 2 = d (dual), 3 = h (hCG)).
Ta tạo ra thêm biến p1 là tỷ lệ tạo blastocytes từ số noãn ban đầu,
bằng cách chia số lượng blastocytes (biến blast) cho số noãn (biến
oocytes).
df = read.csv('Dual_trigger.csv', sep = ';',
dec = ',',
fileEncoding = 'UTF-8-BOM')%>%na.omit()
df$Trigger = factor(df$Trigger)
df$p1 = df$blast/df$oocytes
df%>%head()%>%knitr::kable()
a |
41.49863 |
2 |
1 |
1 |
1.0000000 |
h |
33.70411 |
3 |
3 |
2 |
0.6666667 |
d |
34.57534 |
3 |
3 |
1 |
0.3333333 |
a |
36.05205 |
3 |
2 |
1 |
0.5000000 |
h |
25.75616 |
3 |
3 |
1 |
0.3333333 |
a |
28.35616 |
4 |
4 |
1 |
0.2500000 |
Bước 1: Phân tích mô
tả
Ta kết hợp hàm group_by và summarize của thư viện dplyr để dựng một
bảng thống kê mô tả đơn giản, trình bày trung vị, SD, phân vị thứ 5 và
95 của tỷ lệ tạo blastocytes (biến p1) trong 3 phân nhóm kỹ thuật
trigger.
df%>%group_by(Trigger)%>%
summarize(n = n(),
mean = sprintf("%0.3f", mean(p1)),
SD = sprintf("%0.3f", sd(p1)),
p5 = sprintf("%0.3f", quantile(p1, 0.05)),
p95 = sprintf("%0.3f", quantile(p1, 0.95)),
)%>%knitr::kable()
a |
1263 |
0.278 |
0.181 |
0.062 |
0.600 |
d |
273 |
0.320 |
0.214 |
0.077 |
0.706 |
h |
259 |
0.290 |
0.196 |
0.077 |
0.667 |
Theo kết quả này, mặc dù giá trị trung bình của tỷ lệ tạo blastocyte
có vẻ cao hơn ở nhóm d (dual trigger), nhưng giới hạn phân bố của tỷ lệ
này lại không cho thấy sự khác biệt giữa 3 nhóm.
Sau đây, ta sẽ so sánh trực quan đặc tính phân phối của số lượng
blastocytes (bằng biểu đồ tần suất) và tỷ lệ blastocyte (bằng biểu đồ
boxplot).
Hình 1: Phân bố của số lượng blastocytes giữa 3 nhóm kỹ thuật
trigger
pals = c("d" = "#fc035e",
"h" = "#b814ff",
"a" = "#fcb103")
df %>% ggplot(aes(x = blast,
y = Trigger,
fill= Trigger)) +
geom_density_ridges(stat = "binline",
scale = 1,
bins = 50,
alpha = 0.7,
draw_baseline = FALSE) +
labs(x="Number of Blastocytes", y = "Trigger type") +
scale_fill_manual(values = pals, name = "Trigger") +
scale_x_continuous(breaks = seq(0,30,5))+
coord_flip() +
theme_bw(10) +
geom_vline(xintercept = median(df$blast),
linetype=2,
col="blue")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
theme(axis.text = element_text(size = 12, color = "black"),
axis.title = element_text(size = 15, color = "black"))

Chú thích: 3 biểu đồ tần suất trình bày phân bố của số lượng
blastocytes (trục Y) ở 3 nhóm kỹ thuật trigger (trục X).
Hình 2: Phân bố của tỷ lệ tạo blastocytes từ số noãn ban đầu giữa 3
nhóm
df %>% ggplot(aes(y = p1,
x = Trigger,
fill= Trigger)) +
geom_boxplot(alpha = 0.7) +
labs(y="Blastocyte formation rate", x = "Trigger type") +
scale_fill_manual(values = pals, name = "Trigger") +
scale_y_continuous(breaks = seq(0,1,0.2))+
coord_flip() +
theme_bw(10) +
geom_vline(xintercept = median(df$p1),
linetype=2,
col="blue")+
theme(axis.text = element_text(size = 12, color = "black"),
axis.title = element_text(size = 15, color = "black"))

Chú thích: 3 biểu đồ boxplot trình bày trung vị, phân vị thứ 25,75
của tỷ lệ tạo blastocytes (trục X).Những điểm tròn tương ứng với các giá
trị vượt xa khỏi phân vị thứ 95.
Bước 2: Dựng mô hình
hồi quy Binomial
Hiện thời, kết cục mà ta quan tâm là số lượng \(k\) blastocytes thu được sau quá trình thụ
tinh từ tập hợp \(n\) noãn ban đầu, và
tỷ lệ \(p = k/n\). Ta thấy bài toán này
tương đồng với định nghĩa về quy luật phân phối xác suất Binomial.
Thực vậy, phân phối Binomial (nhị thức) cho phép ước lượng giá trị
của một biến ngẫu nhiên \(Y\), với ý
nghĩa \(Y\) là số lần thu được kết quả
thành công từ \(n\) lượt thử nghiệm độc
lập, biết rằng mỗi thử nghiệm có xác suất thành công là \(p\).
\[Y \sim B(n,p)\] Xác suất \(Y\) nhận một giá trị = k được ước tính bởi
hàm:
\[Pr(k;n,p) =
\binom{n}{k}p^{k}(1-p)^{n-k}\]
Với các tham số có ý nghĩa như sau:
\(k\) là số kết quả thành công (ở
đây là số lượng blastocyte tạo thành)
\(n\) chỉ số lượt thử nghiệm (ở đây
là số noãn được mang đi thụ tinh)
\(n \in
\left\{0,1,2...\right\}\)
\(p\) là xác suất thành công của một
thử nghiệm (xác suất tạo được blastocyte cho mỗi noãn)
\(p \in \left [ 0,1\right ]\)
\((1-p)\) là xác suất của kết quả
đối nghịch (thất bại, không tạo được blastocyte)
Thay vì dùng tham số n và p, ta có thể đặt ra tham số \(\mu = \frac{k}{n}\) như tỷ lệ thu được
blastocyte trên tổng số noãn ban đầu cho mỗi đối tượng bệnh nhân. Ta có
thể ước lượng giá trị của \(\mu\) bằng
một mô hình tuyến tính tổng quát với hàm liên kết logit:
\[logit(\mu)=log(\frac{\mu}{1-\mu}) =
\beta_0+𝛽_1 𝑋_1+𝛽_2 𝑋_2+ … +𝛽_𝑗 𝑋_𝑗\] Ta sẽ dùng mô hình này để
suy diễn thống kê về hiệu ứng của kỹ thuật dual trigger trên tỷ lệ tạo
blastocytes.
Để dựng mô hình binomial, ta dùng hàm gamlss của thư viện gamlss, với
một số lưu ý như sau:
Công thức của mô hình hồi quy binomial khác với các mô hình thông
thường, vì ở vị trí kết quả y, ta cần kết hợp 2 biến: số lượng thành
công (blast) và số lượng thất bại (oocytes - blast), bằng hàm
cbind
Tùy chỉnh family = BI(mu.link = “logit”)
Trong mô hình hiện thời, biến độc lập là Trigger, ta cho nó tương tác
với 2 hiệp biến khác là Age và AFC:
“cbind(blast, oocytes-blast) ~ Trigger * (Age + AFC)”
Nội dung kết quả thô của mô hình như sau
bi_mod = gamlss(cbind(blast, oocytes-blast) ~
Trigger * (Age + AFC),
data = df,
family = BI(mu.link = "logit"))
## GAMLSS-RS iteration 1: Global Deviance = 7646.78
## GAMLSS-RS iteration 2: Global Deviance = 7646.78
summary(bi_mod)
## ******************************************************************
## Family: c("BI", "Binomial")
##
## Call: gamlss(formula = cbind(blast, oocytes - blast) ~ Trigger *
## (Age + AFC), family = BI(mu.link = "logit"), data = df)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: logit
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.0593442 0.1152770 -9.190 < 2e-16 ***
## Triggerd 0.3033475 0.3965697 0.765 0.44442
## Triggerh 0.5956986 0.3634420 1.639 0.10138
## Age -0.0031340 0.0036684 -0.854 0.39304
## AFC 0.0017986 0.0009032 1.991 0.04661 *
## Triggerd:Age -0.0008936 0.0113729 -0.079 0.93738
## Triggerh:Age -0.0091438 0.0106586 -0.858 0.39107
## Triggerd:AFC -0.0165921 0.0076311 -2.174 0.02981 *
## Triggerh:AFC -0.0218670 0.0076811 -2.847 0.00447 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## No. of observations in the fit: 1795
## Degrees of Freedom for the fit: 9
## Residual Deg. of Freedom: 1786
## at cycle: 2
##
## Global Deviance: 7646.78
## AIC: 7664.78
## SBC: 7714.215
## ******************************************************************
Lưu ý rằng mô hình này ước lượng giá trị của \(\mu\) là 1 xác suất (hay tỷ lệ) và sử dụng
hàm liên kết là logit, vì vậy các hệ số hồi quy mà ta thấy đang ở thang
đo \(log(Odds)\), với odds là tỉ lệ
giữa 2 xác suất \(\frac{\mu}{1-\mu}\),
ta có thể chuyển về thang đo odds bằng hàm exponential, hoặc chuyển hẳn
về xác xuất \(\mu\) bằng hàm
plogis.
hệ số (Intercept) tương ứng với \(log(\mu/(1-\mu))\) khi kỹ thuật trigger là
Agonist (nhóm tham chiếu), và Age, AFC ở vị trí trung bình của quần
thể.
Áp dụng hàm plogis cho intercept, ta sẽ có giá trị trung bình của tỷ
lệ blastocyte ở phân nhóm Agonist = 0.257 ở điều kiện Age = 30.55 và AFC
= 16.24
plogis(-1.0593442)
## [1] 0.2574348
Triggerd và Triggerh tương ứng với giá trị log(odds) tăng thêm ở 2
nhóm Dual trigger và hCG so với nhóm tham chiếu Agonist (dĩ nhiên trong
điều kiện AFC và Age không đổi).
Ở đây ta quan tâm đến nhóm Dual trigger, hệ số hồi quy của nó =
0.3033475, giá trị này > 0 nên ta có thể ước lượng là nhóm Dual
trigger có khả năng tạo blastocytes cao hơn so với nhóm tham chiếu
Agonist;
Áp dụng hàm plogis ta có thể ước tính tỷ lệ tạo blastocyte trung bình
ở nhóm Dual trigger = 0.32
plogis(-1.0593442 + 0.3033475)
## [1] 0.3195161
Tuy nhiên, để suy diễn thống kê một cách dễ dàng hơn, ta sẽ tiến hành
quy trình ước tính marginal effects như sau đây.
Bước 3: Suy diễn thống
kê
Đầu tiên, áp dụng hàm avg_comparisons, ta có thể ước tính được khác
biệt về xác suất tạo blastocytes trung bình giữa 2 kỹ thuật: A so với D,
A so với H và D so với H.
dif_p = avg_comparisons(
bi_mod,
what = "mu",
variables = list(Trigger = 'pairwise'))%>%
dplyr::select(-c(1,6))
dif_p %>% knitr::kable()
Trigger |
d - a |
0.0029597 |
0.0103186 |
0.7742380 |
-0.0172643 |
0.0231838 |
Trigger |
h - a |
-0.0040992 |
0.0090471 |
0.6504782 |
-0.0218313 |
0.0136328 |
Trigger |
h - d |
-0.0070590 |
0.0128231 |
0.5819844 |
-0.0321919 |
0.0180739 |
Theo kết quả này, cả 3 cặp so sánh đều không có ý nghĩa thống kê (p
value > 0.05 và KTC95% chứa giá trị 0).
Ta cũng có thể ước tính hiệu ứng của D so với A và H thông qua trị số
Odds-ratio bằng cách áp dụng 2 hàm transform_pre = “lnoravg” và post =
exp.
or = avg_comparisons(
bi_mod,
what = "mu",
variables = list(Trigger = 'pairwise'),
transform_pre = "lnoravg",
transform_post = "exp")%>%
dplyr::select(-c(1))
or %>% knitr::kable()
Trigger |
ln(odds(d) / odds(a)) |
1.0160647 |
0.7734235 |
0.9115953 |
1.132506 |
Trigger |
ln(odds(h) / odds(a)) |
0.9779573 |
0.6512904 |
0.8878552 |
1.077203 |
Trigger |
ln(odds(h) / odds(d)) |
0.9624951 |
0.5810925 |
0.8402912 |
1.102471 |
Theo kết quả này, nếu chọn nhóm Agonist làm tham chiếu, OR của Dual
trigger = 1.016, tuy nhiên KTC95% chứa giá trị 1 và p value > 0.05,
nên không có ý nghĩa thống kê.
Tương tự, OR của nhóm hCG so với Dual trigger = 0.96 < 1, tuy
nhiên cũng không có ý nghĩa thống kê.
Ta còn có thể ước tính hiệu ứng bằng Risk-ratio (RR), chỉ cần thay
hàm transform_pre = “lnratioavg”.
rr = avg_comparisons(
bi_mod,
what = "mu",
variables = list(Trigger = 'pairwise'),
transform_pre = "lnratioavg",
transform_post = exp)%>%
dplyr::select(-c(1))
rr %>% knitr::kable()
Trigger |
ln(mean(d) / mean(a)) |
1.0120817 |
0.7730269 |
0.9327663 |
1.098141 |
Trigger |
ln(mean(h) / mean(a)) |
0.9832669 |
0.6516765 |
0.9138047 |
1.058009 |
Trigger |
ln(mean(h) / mean(d)) |
0.9715292 |
0.5806721 |
0.8768966 |
1.076374 |
Ta diễn giải kết quả RR tương tự như trên, không có sự khác biệt giữa
Dual trigger và nhóm tham chiếu Agonist hoặc hCG.
Ngoài ra, ta có thể đi xa hơn khi ước lượng hiệu ứng (OR hoặc RR) của
Dual trigger trong những điều kiện khác nhau của hiệp biến, thí dụ AFC =
2,5,10 và 20 như sau:
or = avg_comparisons(
bi_mod,
what = "mu",
by = "AFC",
variables = list(Trigger = 'pairwise'),
newdata = datagrid(AFC = c(2,5,10,20),
grid_type = 'counterfactual'),
transform_pre = "lnoravg",
transform_post = "exp")
or %>% knitr::kable()
response |
Trigger |
ln(odds(d) / odds(a)) |
2 |
1.2749243 |
0.0115669 |
1.0558637 |
1.539434 |
response |
Trigger |
ln(odds(d) / odds(a)) |
5 |
1.2130216 |
0.0133671 |
1.0409297 |
1.413565 |
response |
Trigger |
ln(odds(d) / odds(a)) |
10 |
1.1164571 |
0.0499268 |
1.0000352 |
1.246433 |
response |
Trigger |
ln(odds(d) / odds(a)) |
20 |
0.9457769 |
0.4721554 |
0.8124331 |
1.101006 |
response |
Trigger |
ln(odds(h) / odds(a)) |
2 |
1.3143693 |
0.0056288 |
1.0831184 |
1.594993 |
response |
Trigger |
ln(odds(h) / odds(a)) |
5 |
1.2309708 |
0.0089146 |
1.0534511 |
1.438405 |
response |
Trigger |
ln(odds(h) / odds(a)) |
10 |
1.1035630 |
0.0691433 |
0.9923044 |
1.227296 |
response |
Trigger |
ln(odds(h) / odds(a)) |
20 |
0.8869339 |
0.0901621 |
0.7720058 |
1.018971 |
response |
Trigger |
ln(odds(h) / odds(d)) |
2 |
1.0309391 |
0.8183369 |
0.7948992 |
1.337069 |
response |
Trigger |
ln(odds(h) / odds(d)) |
5 |
1.0147971 |
0.8896481 |
0.8246449 |
1.248796 |
response |
Trigger |
ln(odds(h) / odds(d)) |
10 |
0.9884509 |
0.8716356 |
0.8585411 |
1.138018 |
response |
Trigger |
ln(odds(h) / odds(d)) |
20 |
0.9377834 |
0.5286725 |
0.7679211 |
1.145219 |
Kết quả này khá thú vị, vì nó cho thấy rằng khi AFC <= 5, OR của
Dual trigger so với Agonist lần lượt là 1.27 và 1.21 và có ý nghĩa thống
kê.
Ta có thể trình bày trực quan về hiệu ứng của kỹ thuật trigger đối
với tỷ lệ tạo blastocyte, dựa vào mô hình Binomial như sau:
preds = predictions(bi_mod,
what = "mu",
newdata = datagrid(Trigger = c('d','a','h'),
grid_type = "counterfactual"),
new_data = df)
preds%>%ggplot()+
stat_halfeye(alpha = 0.4,
aes(x = Trigger,
y = estimate,
fill = Trigger),
.width = c(0.75, 0.95),
point_interval = 'median_qi',
show.legend = T)+
stat_lineribbon(aes(y = estimate, x = Trigger),
fill = 'gold',
.width = c(.95, .75, .50),
alpha = 1/8,
show.legend = F) +
labs(y="Rate of blastocytes", x = "Trigger") +
scale_fill_manual(values = pals, name = "Trigger")+
scale_y_continuous(breaks = c(seq(0,0.4,0.05)))+
theme_bw()

Ta có thể trình bày cùng kết quả trên nhưng cho riêng 6 điều kiện
khác nhau về giá trị AFC = 1,5,10,15,20 và 30
preds = predictions(model = bi_mod,
what = "mu",
newdata = datagrid(AFC = c(1,5,10,15,20,30),
grid_type = "counterfactual"))
preds%>%ggplot()+
stat_halfeye(alpha = 0.4,
aes(x = Trigger,
y = estimate,
fill = Trigger),
.width = c(0.75, 0.95),
point_interval = 'median_qi',
show.legend = T)+
stat_lineribbon(aes(y = estimate, x = Trigger),
fill = 'gold',
.width = c(.95, .75, .50),
alpha = 1/8,
show.legend = F) +
labs(y="Rate of blastocytes", x = "Trigger") +
scale_fill_manual(values = pals, name = "Trigger")+
facet_wrap(~AFC, scales = "free")+
theme_bw()

Diễn giải kết quả của
phân tích
Kết quả phân tích hồi quy cho thấy ở cấp độ quần thể, kỹ thuật dual
trigger có hiệu quả tương đương với 2 ký thuật Agonist và hCG về tỷ lệ
tạo blastocytes. Tuy nhiên, ở những đối tượng có AFC <5, hiệu quả tạo
blastocyte của Dual trigger cao hơn một cách có ý nghĩa so với Agonist
(OR = 1.27, KTC95%: 1.05 - 1.54, p=0.01).
LS0tDQp0aXRsZTogIkjhu5NpIHF1eSBCaW5vbWlhbCINCmF1dGhvcjogIkJTLiBOZ+G7jWMgQ2jDonUsIEJTLiBLaOG6oyBOaGkiDQpkYXRlOiAiMjggVGjDoW5nIDIgbsSDbSAyMDIzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IGRlZmF1bHQNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZGV2OiBzdmcNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgbGF0ZXhfZW5naW5lOiBsdWFsYXRleA0KICAgIGtlZXBfdGV4OiB5ZXMNCi0tLQ0KDQohW10oYmlub21pYWxfcmVnLnBuZykNCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGKQ0KYGBgDQoNCiMgR2nhu5tpIHRoaeG7h3UNCg0KTmjhu69uZyBuZ2hpw6puIGPhu6l1IHbhu4EgcXV5IHRyw6xuaCB0aOG7pSB0aW5oIOG7kW5nIG5naGnhu4dtIMSR4bq3dCByYSB5w6p1IGPhuqd1IGto4bqjbyBzw6F0IG5oaeG7gXUgbG/huqFpIGvhur90IGPhu6VjIGzDom0gc8OgbmcgduG7m2kgYuG6o24gY2jhuqV0IGtow6FjIG5oYXUuIE3hurd0IGtow6FjLCBjw7MgdMOtbmggY2jhuqV0IGvhur8gdGjhu6thIHRyb25nIGNodeG7l2kga+G6v3QgcXXhuqMgY+G7p2EgcXV5IHRyw6xuaC4gVGjDoG5oIHBo4bqpbSB0aHUgxJHGsOG7o2MgY+G7p2EgY8O0bmcgxJFv4bqhbiBzYXUgY2jDrW5oIGzDoCBo4buHIHF14bqjIHThu6sgdmnhu4djIHRp4bq/biBow6BuaCBt4buZdCBsb+G6oXQgcGjDqXAgdGjhu60gKHRow60gZOG7pSB0aOG7pSB0aW5oLCBjaHV54buDbiBwaMO0aS4uLikgdHLDqm4gdOG6rXAgaOG7o3Aga+G6v3QgcXXhuqMgY8O0bmcgxJFv4bqhbiB0csaw4bubYy4gVGjDrSBk4bulLCBz4buRIGzGsOG7o25nIHBow7RpIGzDoCBo4buHIHF14bqjIHThu6sgdOG7tyBs4buHIHRo4bulIHRpbmggdGjDoG5oIGPDtG5nIHRyw6puIHThuq1wIGjhu6NwIG5oaeG7gXUgbm/Do24uIERvIMSRw7MsIGPDuW5nIG3hu5l0IHRo4buxYyB0aOG7gyBr4bq/dCBj4bulYyBuaMawbmcgY8OzIHRo4buDIMSRxrDhu6NjIGRp4buFbiDEkeG6oXQgdGhlbyBuaGnhu4F1IGPDoWNoOyBz4buRIGzGsOG7o25nIHBow7RpLCB04bu3IGzhu4cgdGjhu6UgdGluaCB0csOqbiB04buVbmcgc+G7kSBub8OjbiwgeMOhYyBzdeG6pXQgdGjhu6UgdGluaCB0aMOgbmggY8O0bmcgY2hvIG3hu5dpIG5vw6NuLi4uIA0KDQpI4bqndSBo4bq/dCBuaOG7r25nIG5naGnDqm4gY+G7qXUgaGnhu4duIG5heSBjaOG7iSBt4bubaSBk4burbmcgbOG6oWkg4bufIHPhu7Egc28gc8OhbmggdsOgIGtp4buDbSDEkeG7i25oIGLhurFuZyBuaOG7r25nIHBow6lwIGtp4buDbSB0aOG7kW5nIGvDqiB0aMO0IHPGoSBuaMawIE1hbm4tV2hpdG5leSwgU3R1ZGVudCB0IHRlc3QsIGNobyBt4buNaSBr4bq/dCBxdeG6oyBsw6AgY29uIHPhu5EsIGtow7RuZyBwaMOibiBiaeG7h3QgYuG6o24gY2jhuqV0IGPhu6dhIGNow7puZyBsYSBz4buRIMSR4bq/bSwgc+G7kSBsacOqbiB04bulYyBoYXkgdOG7tyBs4buHLiANCg0KVHJvbmcgY2jGsMahbmcgbsOgeSwgY2jDum5nIHTDtGkgZ2nhu5tpIHRoaeG7h3UgbeG7mXQgcGjGsMahbmcgcGjDoXAgcGjDom4gdMOtY2ggaOG7o3AgbMO9IHbDoCBjaMOtbmggeMOhYyBoxqFuIGNobyBr4bq/dCBj4bulYyBsw6AgdOG7tyBs4buHLCDDoXAgZOG7pW5nIGzDvSB0aHV54bq/dCB4w6FjIHN14bqldCBjaG8gYmnhur9uIHLhu51pIHLhuqFjIHbDoCBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0IHbhu5tpIHBow6JuIHBo4buRaSBuaOG7iyB0aOG7qWMgKEJpbm9taWFsKS4NCg0KVMOsbmggaHXhu5FuZyBtaW5oIGjhu41hIG5oxrAgc2F1OiBN4buZdCBiw6FjIHPEqSBtdeG7kW4ga2jhuqNvIHPDoXQgaGnhu4d1IHF14bqjIHThuqFvIHBow7RpIChibGFzdG9jeXRlcykgY+G7p2Ega+G7uSB0aHXhuq10IGvDrWNoIHRow61jaCB0csaw4bufbmcgdGjDoG5oIG5vw6NuIGvDqXAgKER1YWwgdHJpZ2dlciksIHNvIHbhu5tpIDIga+G7uSB0aHXhuq10IHRoYW0gY2hp4bq/dSBkw7luZyBhZ29uaXN0IHbDoCBoQ0cgxJHGoW4gZ2nhuqNuLg0KDQojIEvhur8gaG/huqFjaCBwaMOibiB0w61jaA0KDQpLaOG6oyBuxINuZyB04bqhbyBibGFzdG9jeXRlIGtoaSBkw7luZyBr4bu5IHRodeG6rXQgRHVhbCB0cmlnZ2VyIHNvIHbhu5tpIDIgcGjGsMahbmcgcGjDoXAgdGhhbSBjaGnhur91ICjEkeG7k25nIHbhuq1uIGhv4bq3YyBoQ0cgxJHGoW4pIHPhur0gxJHGsOG7o2MgxrDhu5tjIGzGsOG7o25nIGLhurFuZyBtYXJnaW5hbCBPZGRzLXJhdGlvIChPUikgdsOgIHJpc2stcmF0aW8gKFJSKSwgY8OzIGhp4buHdSBjaOG7iW5oIGNobyBUdeG7lWkgdsOgIEFGQywgZOG7sWEgdsOgbyBt4buZdCBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0IChHTE0pIHbhu5tpIHBow6JuIHBo4buRaSBuaOG7iyB0aOG7qWMgKEJpbm9taWFsKS4NCg0KU3V5IGRp4buFbiB0aOG7kW5nIGvDqiBk4buxYSB2w6BvIHBo4bunIG5o4bqtbiBnaeG6oyB0aHV54bq/dCB2w7QgaGnhu4d1IChPUiB2w6AgUlIgPSAxKSDhu58gbmfGsOG7oW5nIMO9IG5naMSpYSBwID0gMC4wNS4NCg0KIyBDw7RuZyBj4bulIGPhuqduIHRoaeG6v3QgY2hvIHF1eSB0csOsbmgNCg0KKyBUaMawIHZp4buHbiBkcGx5ciB0cm9uZyBo4buHIHNpbmggdGjDoWkgdGlkeXZlcnNlIMSR4buDIHRoYW8gdMOhYyBk4buvIGxp4buHdSB2w6AgdGjhu5FuZyBrw6ogbcO0IHThuqMNCg0KKyBUaMawIHZp4buHbiBnZ3Bsb3QyLCBnZ3JpZGVzLCB0aWR5YmF5ZXMgdsOgIGdnZGlzdCDEkeG7gyB24bq9IG3hu5l0IHPhu5EgYmnhu4N1IMSR4buTIHRo4buRbmcga8OqOw0KDQorIFRoxrAgdmnhu4duIGdhbWxzcyDEkeG7gyBk4buxbmcgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgQmlub21pYWwNCg0KKyBUaMawIHZp4buHbiBtYXJnaW5hbGVmZmVjdHMgxJHhu4MgxrDhu5tjIHTDrW5oIE9SLCBSUiB2w6Agc3V5IGRp4buFbiB0aOG7kW5nIGvDqiB04burIGvhur90IHF14bqjIG3DtCBow6xuaC4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dyaWRnZXMpDQoNCmxpYnJhcnkoZ2FtbHNzKQ0KbGlicmFyeShtYXJnaW5hbGVmZmVjdHMpDQoNCmxpYnJhcnkodGlkeWJheWVzKQ0KbGlicmFyeShnZ2Rpc3QpDQpgYGANCg0KxJDhuqd1IHRpw6puLCB0YSB04bqjaSBk4buvIGxp4buHdSB04burIGZpbGUgRHVhbF90cmlnZ2VyLmNzdiB2w6BvIGRhdGFmcmFtZSBkZiBi4bqxbmcgaMOgbSByZWFkLmNzdiwgc2F1IMSRw7Mgw6FwIGThu6VuZyBow6BtIGZhY3RvciDEkeG7gyBjaHV54buDbiBk4bqhbmcgYmnhur9uIFRyaWdnZXIgdGjDoG5oIHPhu5EgdGjhu6kgdOG7sSAoMSA9IGEgKGFnb25pc3QpLCAyID0gZCAoZHVhbCksIDMgPSBoIChoQ0cpKS4NCg0KVGEgdOG6oW8gcmEgdGjDqm0gYmnhur9uIHAxIGzDoCB04bu3IGzhu4cgdOG6oW8gYmxhc3RvY3l0ZXMgdOG7qyBz4buRIG5vw6NuIGJhbiDEkeG6p3UsIGLhurFuZyBjw6FjaCBjaGlhIHPhu5EgbMaw4bujbmcgYmxhc3RvY3l0ZXMgKGJp4bq/biBibGFzdCkgY2hvIHPhu5Egbm/Do24gKGJp4bq/biBvb2N5dGVzKS4NCg0KYGBge3J9DQpkZiA9IHJlYWQuY3N2KCdEdWFsX3RyaWdnZXIuY3N2Jywgc2VwID0gJzsnLCANCiAgICAgICAgICAgICAgZGVjID0gJywnLCANCiAgICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gJ1VURi04LUJPTScpJT4lbmEub21pdCgpDQoNCmRmJFRyaWdnZXIgPSBmYWN0b3IoZGYkVHJpZ2dlcikNCmRmJHAxID0gZGYkYmxhc3QvZGYkb29jeXRlcw0KDQpkZiU+JWhlYWQoKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KIyBCxrDhu5tjIDE6IFBow6JuIHTDrWNoIG3DtCB04bqjDQoNClRhIGvhur90IGjhu6NwIGjDoG0gZ3JvdXBfYnkgdsOgIHN1bW1hcml6ZSBj4bunYSB0aMawIHZp4buHbiBkcGx5ciDEkeG7gyBk4buxbmcgbeG7mXQgYuG6o25nIHRo4buRbmcga8OqIG3DtCB04bqjIMSRxqFuIGdp4bqjbiwgdHLDrG5oIGLDoHkgdHJ1bmcgduG7iywgU0QsIHBow6JuIHbhu4sgdGjhu6kgNSB2w6AgOTUgY+G7p2EgdOG7tyBs4buHIHThuqFvIGJsYXN0b2N5dGVzIChiaeG6v24gcDEpIHRyb25nIDMgcGjDom4gbmjDs20ga+G7uSB0aHXhuq10IHRyaWdnZXIuDQoNCmBgYHtyfQ0KZGYlPiVncm91cF9ieShUcmlnZ2VyKSU+JQ0KICBzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIG1lYW4gPSBzcHJpbnRmKCIlMC4zZiIsIG1lYW4ocDEpKSwNCiAgICAgICAgICAgIFNEID0gc3ByaW50ZigiJTAuM2YiLCBzZChwMSkpLA0KICAgICAgICAgICAgcDUgPSBzcHJpbnRmKCIlMC4zZiIsIHF1YW50aWxlKHAxLCAwLjA1KSksDQogICAgICAgICAgICBwOTUgPSBzcHJpbnRmKCIlMC4zZiIsIHF1YW50aWxlKHAxLCAwLjk1KSksDQogICAgICAgICAgICApJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpUaGVvIGvhur90IHF14bqjIG7DoHksIG3hurdjIGTDuSBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIHThu7cgbOG7hyB04bqhbyBibGFzdG9jeXRlIGPDsyB24bq7IGNhbyBoxqFuIOG7nyBuaMOzbSBkIChkdWFsIHRyaWdnZXIpLCBuaMawbmcgZ2nhu5tpIGjhuqFuIHBow6JuIGLhu5EgY+G7p2EgdOG7tyBs4buHIG7DoHkgbOG6oWkga2jDtG5nIGNobyB0aOG6pXkgc+G7sSBraMOhYyBiaeG7h3QgZ2nhu69hIDMgbmjDs20uDQoNClNhdSDEkcOieSwgdGEgc+G6vSBzbyBzw6FuaCB0cuG7sWMgcXVhbiDEkeG6t2MgdMOtbmggcGjDom4gcGjhu5FpIGPhu6dhIHPhu5EgbMaw4bujbmcgYmxhc3RvY3l0ZXMgKGLhurFuZyBiaeG7g3UgxJHhu5MgdOG6p24gc3XhuqV0KSB2w6AgdOG7tyBs4buHIGJsYXN0b2N5dGUgKGLhurFuZyBiaeG7g3UgxJHhu5MgYm94cGxvdCkuDQoNCkjDrG5oIDE6IFBow6JuIGLhu5EgY+G7p2Egc+G7kSBsxrDhu6NuZyBibGFzdG9jeXRlcyBnaeG7r2EgMyBuaMOzbSBr4bu5IHRodeG6rXQgdHJpZ2dlcg0KDQpgYGB7cn0NCnBhbHMgPSBjKCJkIiA9ICIjZmMwMzVlIiwgDQogICAgICAgICAiaCIgPSAiI2I4MTRmZiIsIA0KICAgICAgICAgImEiID0gIiNmY2IxMDMiKQ0KDQoNCmRmICU+JSBnZ3Bsb3QoYWVzKHggPSBibGFzdCwgDQogICAgICAgICAgICAgICAgICAgICAgeSA9IFRyaWdnZXIsIA0KICAgICAgICAgICAgICAgICAgICAgIGZpbGw9IFRyaWdnZXIpKSArIA0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKHN0YXQgPSAiYmlubGluZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC43LA0KICAgICAgICAgICAgICAgICAgICAgIGRyYXdfYmFzZWxpbmUgPSBGQUxTRSkgKw0KICBsYWJzKHg9Ik51bWJlciBvZiBCbGFzdG9jeXRlcyIsIHkgPSAiVHJpZ2dlciB0eXBlIikgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFscywgbmFtZSA9ICJUcmlnZ2VyIikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMzAsNSkpKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9idygxMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWRpYW4oZGYkYmxhc3QpLA0KICAgICAgICAgICAgIGxpbmV0eXBlPTIsDQogICAgICAgICAgICAgY29sPSJibHVlIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIGNvbG9yID0gImJsYWNrIikpDQpgYGANCg0KQ2jDuiB0aMOtY2g6IDMgYmnhu4N1IMSR4buTIHThuqduIHN14bqldCB0csOsbmggYsOgeSBwaMOibiBi4buRIGPhu6dhIHPhu5EgbMaw4bujbmcgYmxhc3RvY3l0ZXMgKHRy4bulYyBZKSDhu58gMyBuaMOzbSBr4bu5IHRodeG6rXQgdHJpZ2dlciAodHLhu6VjIFgpLg0KDQpIw6xuaCAyOiBQaMOibiBi4buRIGPhu6dhIHThu7cgbOG7hyB04bqhbyBibGFzdG9jeXRlcyB04burIHPhu5Egbm/Do24gYmFuIMSR4bqndSBnaeG7r2EgMyBuaMOzbQ0KDQpgYGB7cn0NCmRmICU+JSBnZ3Bsb3QoYWVzKHkgPSBwMSwgDQogICAgICAgICAgICAgICAgICAgICAgeCA9IFRyaWdnZXIsIA0KICAgICAgICAgICAgICAgICAgICAgIGZpbGw9IFRyaWdnZXIpKSArIA0KICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjcpICsNCiAgbGFicyh5PSJCbGFzdG9jeXRlIGZvcm1hdGlvbiByYXRlIiwgeCA9ICJUcmlnZ2VyIHR5cGUiKSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxzLCBuYW1lID0gIlRyaWdnZXIiKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxLDAuMikpKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9idygxMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWRpYW4oZGYkcDEpLA0KICAgICAgICAgICAgIGxpbmV0eXBlPTIsDQogICAgICAgICAgICAgY29sPSJibHVlIikrDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBjb2xvciA9ICJibGFjayIpKQ0KYGBgDQoNCkNow7ogdGjDrWNoOiAzIGJp4buDdSDEkeG7kyBib3hwbG90IHRyw6xuaCBiw6B5IHRydW5nIHbhu4ssIHBow6JuIHbhu4sgdGjhu6kgMjUsNzUgY+G7p2EgdOG7tyBs4buHIHThuqFvIGJsYXN0b2N5dGVzICh0cuG7pWMgWCkuTmjhu69uZyDEkWnhu4NtIHRyw7JuIHTGsMahbmcg4bupbmcgduG7m2kgY8OhYyBnacOhIHRy4buLIHbGsOG7o3QgeGEga2jhu49pIHBow6JuIHbhu4sgdGjhu6kgOTUuIA0KDQojIELGsOG7m2MgMjogROG7sW5nIG3DtCBow6xuaCBo4buTaSBxdXkgQmlub21pYWwNCg0KSGnhu4duIHRo4budaSwga+G6v3QgY+G7pWMgbcOgIHRhIHF1YW4gdMOibSBsw6Agc+G7kSBsxrDhu6NuZyAkayQgYmxhc3RvY3l0ZXMgdGh1IMSRxrDhu6NjIHNhdSBxdcOhIHRyw6xuaCB0aOG7pSB0aW5oIHThu6sgdOG6rXAgaOG7o3AgJG4kIG5vw6NuIGJhbiDEkeG6p3UsIHbDoCB04bu3IGzhu4cgJHAgPSBrL24kLiBUYSB0aOG6pXkgYsOgaSB0b8OhbiBuw6B5IHTGsMahbmcgxJHhu5NuZyB24bubaSDEkeG7i25oIG5naMSpYSB24buBIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIHjDoWMgc3XhuqV0IEJpbm9taWFsLiANCg0KVGjhu7FjIHbhuq15LCBwaMOibiBwaOG7kWkgQmlub21pYWwgKG5o4buLIHRo4bupYykgY2hvIHBow6lwIMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIGPhu6dhIG3hu5l0IGJp4bq/biBuZ+G6q3Ugbmhpw6puICRZJCwgduG7m2kgw70gbmdoxKlhICRZJCBsw6Agc+G7kSBs4bqnbiB0aHUgxJHGsOG7o2Mga+G6v3QgcXXhuqMgdGjDoG5oIGPDtG5nIHThu6sgJG4kIGzGsOG7o3QgdGjhu60gbmdoaeG7h20gxJHhu5ljIGzhuq1wLCBiaeG6v3QgcuG6sW5nIG3hu5dpIHRo4butIG5naGnhu4dtIGPDsyB4w6FjIHN14bqldCB0aMOgbmggY8O0bmcgbMOgICRwJC4NCg0KJCRZIFxzaW0gQihuLHApJCQNCljDoWMgc3XhuqV0ICRZJCBuaOG6rW4gbeG7mXQgZ2nDoSB0cuG7iyA9IGsgxJHGsOG7o2MgxrDhu5tjIHTDrW5oIGLhu59pIGjDoG06DQoNCiQkUHIoaztuLHApID0gXGJpbm9te259e2t9cF57a30oMS1wKV57bi1rfSQkDQoNClbhu5tpIGPDoWMgdGhhbSBz4buRIGPDsyDDvSBuZ2jEqWEgbmjGsCBzYXU6DQoNCiRrJCBsw6Agc+G7kSBr4bq/dCBxdeG6oyB0aMOgbmggY8O0bmcgKOG7nyDEkcOieSBsw6Agc+G7kSBsxrDhu6NuZyBibGFzdG9jeXRlIHThuqFvIHRow6BuaCkNCg0KJG4kIGNo4buJIHPhu5EgbMaw4bujdCB0aOG7rSBuZ2hp4buHbSAo4bufIMSRw6J5IGzDoCBz4buRIG5vw6NuIMSRxrDhu6NjIG1hbmcgxJFpIHRo4bulIHRpbmgpDQoNCiRuIFxpbiBcbGVmdFx7MCwxLDIuLi5ccmlnaHRcfSQgDQoNCiRwJCBsw6AgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nIGPhu6dhIG3hu5l0IHRo4butIG5naGnhu4dtICh4w6FjIHN14bqldCB04bqhbyDEkcaw4bujYyBibGFzdG9jeXRlIGNobyBt4buXaSBub8OjbikNCg0KJHAgXGluIFxsZWZ0IFsgMCwxXHJpZ2h0IF0kDQoNCiQoMS1wKSQgbMOgIHjDoWMgc3XhuqV0IGPhu6dhIGvhur90IHF14bqjIMSR4buRaSBuZ2jhu4tjaCAodGjhuqV0IGLhuqFpLCBraMO0bmcgdOG6oW8gxJHGsOG7o2MgYmxhc3RvY3l0ZSkNCg0KVGhheSB2w6wgZMO5bmcgdGhhbSBz4buRIG4gdsOgIHAsIHRhIGPDsyB0aOG7gyDEkeG6t3QgcmEgdGhhbSBz4buRICRcbXUgPSBcZnJhY3trfXtufSQgbmjGsCB04bu3IGzhu4cgdGh1IMSRxrDhu6NjIGJsYXN0b2N5dGUgdHLDqm4gdOG7lW5nIHPhu5Egbm/Do24gYmFuIMSR4bqndSBjaG8gbeG7l2kgxJHhu5FpIHTGsOG7o25nIGLhu4duaCBuaMOibi4gVGEgY8OzIHRo4buDIMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIGPhu6dhICRcbXUkIGLhurFuZyBt4buZdCBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgduG7m2kgaMOgbSBsacOqbiBr4bq/dCBsb2dpdDoNCg0KJCRsb2figaFpdChcbXUpPWxvZyhcZnJhY3tcbXV9ezEtXG11fSkgPSBcYmV0YV8wK/Cdm71fMSDwnZGLXzEr8J2bvV8yIPCdkYtfMisg4oCmICvwnZu9X/CdkZcg8J2Ri1/wnZGXJCQNClRhIHPhur0gZMO5bmcgbcO0IGjDrG5oIG7DoHkgxJHhu4Mgc3V5IGRp4buFbiB0aOG7kW5nIGvDqiB24buBIGhp4buHdSDhu6luZyBj4bunYSBr4bu5IHRodeG6rXQgZHVhbCB0cmlnZ2VyIHRyw6puIHThu7cgbOG7hyB04bqhbyBibGFzdG9jeXRlcy4NCg0KxJDhu4MgZOG7sW5nIG3DtCBow6xuaCBiaW5vbWlhbCwgdGEgZMO5bmcgaMOgbSBnYW1sc3MgY+G7p2EgdGjGsCB2aeG7h24gZ2FtbHNzLCB24bubaSBt4buZdCBz4buRIGzGsHUgw70gbmjGsCBzYXU6DQoNCisgQ8O0bmcgdGjhu6ljIGPhu6dhIG3DtCBow6xuaCBo4buTaSBxdXkgYmlub21pYWwga2jDoWMgduG7m2kgY8OhYyBtw7QgaMOsbmggdGjDtG5nIHRoxrDhu51uZywgdsOsIOG7nyB24buLIHRyw60ga+G6v3QgcXXhuqMgeSwgdGEgY+G6p24ga+G6v3QgaOG7o3AgMiBiaeG6v246IHPhu5EgbMaw4bujbmcgdGjDoG5oIGPDtG5nIChibGFzdCkgdsOgIHPhu5EgbMaw4bujbmcgdGjhuqV0IGLhuqFpIChvb2N5dGVzIC0gYmxhc3QpLCBi4bqxbmcgaMOgbSBjYmluZA0KDQorIFTDuXkgY2jhu4luaCBmYW1pbHkgPSBCSShtdS5saW5rID0gImxvZ2l0IikNCg0KVHJvbmcgbcO0IGjDrG5oIGhp4buHbiB0aOG7nWksIGJp4bq/biDEkeG7mWMgbOG6rXAgbMOgIFRyaWdnZXIsIHRhIGNobyBuw7MgdMawxqFuZyB0w6FjIHbhu5tpIDIgaGnhu4dwIGJp4bq/biBraMOhYyBsw6AgQWdlIHbDoCBBRkM6DQoNCiJjYmluZChibGFzdCwgb29jeXRlcy1ibGFzdCkgfiAgVHJpZ2dlciAqIChBZ2UgKyBBRkMpIg0KDQpO4buZaSBkdW5nIGvhur90IHF14bqjIHRow7QgY+G7p2EgbcO0IGjDrG5oIG5oxrAgc2F1DQpgYGB7cn0NCmJpX21vZCA9IGdhbWxzcyhjYmluZChibGFzdCwgb29jeXRlcy1ibGFzdCkgfiANCiAgICAgICAgICAgICAgIFRyaWdnZXIgKiAoQWdlICsgQUZDKSwNCiAgICAgICAgICAgICBkYXRhID0gZGYsDQogICAgICAgICAgICAgZmFtaWx5ID0gQkkobXUubGluayA9ICJsb2dpdCIpKQ0KDQpzdW1tYXJ5KGJpX21vZCkNCmBgYA0KDQpMxrB1IMO9IHLhurFuZyBtw7QgaMOsbmggbsOgeSDGsOG7m2MgbMaw4bujbmcgZ2nDoSB0cuG7iyBj4bunYSAkXG11JCBsw6AgMSB4w6FjIHN14bqldCAoaGF5IHThu7cgbOG7hykgdsOgIHPhu60gZOG7pW5nIGjDoG0gbGnDqm4ga+G6v3QgbMOgIGxvZ2l0LCB2w6wgduG6rXkgY8OhYyBo4buHIHPhu5EgaOG7k2kgcXV5IG3DoCB0YSB0aOG6pXkgxJFhbmcg4bufIHRoYW5nIMSRbyAkbG9nKE9kZHMpJCwgduG7m2kgb2RkcyBsw6AgdOG7iSBs4buHIGdp4buvYSAyIHjDoWMgc3XhuqV0ICRcZnJhY3tcbXV9ezEtXG11fSQsIHRhIGPDsyB0aOG7gyBjaHV54buDbiB24buBIHRoYW5nIMSRbyBvZGRzIGLhurFuZyBow6BtIGV4cG9uZW50aWFsLCBob+G6t2MgY2h1eeG7g24gaOG6s24gduG7gSB4w6FjIHh14bqldCAkXG11JCBi4bqxbmcgaMOgbSBwbG9naXMuDQoNCmjhu4cgc+G7kSAoSW50ZXJjZXB0KSB0xrDGoW5nIOG7qW5nIHbhu5tpICRsb2coXG11LygxLVxtdSkpJCBraGkga+G7uSB0aHXhuq10IHRyaWdnZXIgbMOgIEFnb25pc3QgKG5ow7NtIHRoYW0gY2hp4bq/dSksIHbDoCBBZ2UsIEFGQyDhu58gduG7iyB0csOtIHRydW5nIGLDrG5oIGPhu6dhIHF14bqnbiB0aOG7gy4NCg0Kw4FwIGThu6VuZyBow6BtIHBsb2dpcyBjaG8gaW50ZXJjZXB0LCB0YSBz4bq9IGPDsyBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIHThu7cgbOG7hyBibGFzdG9jeXRlIOG7nyBwaMOibiBuaMOzbSBBZ29uaXN0ID0gMC4yNTcg4bufIMSRaeG7gXUga2nhu4duIEFnZSA9IDMwLjU1IHbDoCBBRkMgPSAxNi4yNA0KDQpgYGB7cn0NCnBsb2dpcygtMS4wNTkzNDQyKQ0KYGBgDQoNClRyaWdnZXJkIHbDoCBUcmlnZ2VyaCB0xrDGoW5nIOG7qW5nIHbhu5tpIGdpw6EgdHLhu4sgbG9nKG9kZHMpIHTEg25nIHRow6ptIOG7nyAyIG5ow7NtIER1YWwgdHJpZ2dlciB2w6AgaENHIHNvIHbhu5tpIG5ow7NtIHRoYW0gY2hp4bq/dSBBZ29uaXN0IChkxKkgbmhpw6puIHRyb25nIMSRaeG7gXUga2nhu4duIEFGQyB2w6AgQWdlIGtow7RuZyDEkeG7lWkpLg0KDQrhu54gxJHDonkgdGEgcXVhbiB0w6JtIMSR4bq/biBuaMOzbSBEdWFsIHRyaWdnZXIsIGjhu4cgc+G7kSBo4buTaSBxdXkgY+G7p2EgbsOzID0gMC4zMDMzNDc1LCBnacOhIHRy4buLIG7DoHkgPiAwIG7Dqm4gdGEgY8OzIHRo4buDIMaw4bubYyBsxrDhu6NuZyBsw6AgbmjDs20gRHVhbCB0cmlnZ2VyIGPDsyBraOG6oyBuxINuZyB04bqhbyBibGFzdG9jeXRlcyBjYW8gaMahbiBzbyB24bubaSBuaMOzbSB0aGFtIGNoaeG6v3UgQWdvbmlzdDsgDQoNCsOBcCBk4bulbmcgaMOgbSBwbG9naXMgdGEgY8OzIHRo4buDIMaw4bubYyB0w61uaCB04bu3IGzhu4cgdOG6oW8gYmxhc3RvY3l0ZSB0cnVuZyBiw6xuaCDhu58gbmjDs20gRHVhbCB0cmlnZ2VyID0gMC4zMg0KDQpgYGB7cn0NCnBsb2dpcygtMS4wNTkzNDQyICsgMC4zMDMzNDc1KQ0KYGBgDQpUdXkgbmhpw6puLCDEkeG7gyBzdXkgZGnhu4VuIHRo4buRbmcga8OqIG3hu5l0IGPDoWNoIGThu4UgZMOgbmcgaMahbiwgdGEgc+G6vSB0aeG6v24gaMOgbmggcXV5IHRyw6xuaCDGsOG7m2MgdMOtbmggbWFyZ2luYWwgZWZmZWN0cyBuaMawIHNhdSDEkcOieS4NCg0KIyBCxrDhu5tjIDM6IFN1eSBkaeG7hW4gdGjhu5FuZyBrw6oNCg0KxJDhuqd1IHRpw6puLCDDoXAgZOG7pW5nIGjDoG0gYXZnX2NvbXBhcmlzb25zLCB0YSBjw7MgdGjhu4MgxrDhu5tjIHTDrW5oIMSRxrDhu6NjIGtow6FjIGJp4buHdCB24buBIHjDoWMgc3XhuqV0IHThuqFvIGJsYXN0b2N5dGVzIHRydW5nIGLDrG5oIGdp4buvYSAyIGvhu7kgdGh14bqtdDogQSBzbyB24bubaSBELCBBIHNvIHbhu5tpIEggdsOgIEQgc28gduG7m2kgSC4NCg0KYGBge3J9DQpkaWZfcCA9IGF2Z19jb21wYXJpc29ucygNCiAgYmlfbW9kLA0KICB3aGF0ID0gIm11IiwNCiAgdmFyaWFibGVzID0gbGlzdChUcmlnZ2VyID0gJ3BhaXJ3aXNlJykpJT4lDQogIGRwbHlyOjpzZWxlY3QoLWMoMSw2KSkNCg0KZGlmX3AgJT4lIGtuaXRyOjprYWJsZSgpDQpgYGANCg0KVGhlbyBr4bq/dCBxdeG6oyBuw6B5LCBj4bqjIDMgY+G6t3Agc28gc8OhbmggxJHhu4F1IGtow7RuZyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChwIHZhbHVlID4gMC4wNSB2w6AgS1RDOTUlIGNo4bupYSBnacOhIHRy4buLIDApLiANCg0KVGEgY8WpbmcgY8OzIHRo4buDIMaw4bubYyB0w61uaCBoaeG7h3Ug4bupbmcgY+G7p2EgRCBzbyB24bubaSBBIHbDoCBIIHRow7RuZyBxdWEgdHLhu4sgc+G7kSBPZGRzLXJhdGlvIGLhurFuZyBjw6FjaCDDoXAgZOG7pW5nIDIgaMOgbSB0cmFuc2Zvcm1fcHJlID0gImxub3JhdmciIHbDoCBwb3N0ID0gZXhwLg0KDQpgYGB7cn0NCm9yID0gYXZnX2NvbXBhcmlzb25zKA0KICBiaV9tb2QsDQogIHdoYXQgPSAibXUiLA0KICB2YXJpYWJsZXMgPSBsaXN0KFRyaWdnZXIgPSAncGFpcndpc2UnKSwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbm9yYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSAiZXhwIiklPiUNCiAgZHBseXI6OnNlbGVjdCgtYygxKSkNCg0Kb3IgJT4lIGtuaXRyOjprYWJsZSgpDQpgYGANCg0KVGhlbyBr4bq/dCBxdeG6oyBuw6B5LCBu4bq/dSBjaOG7jW4gbmjDs20gQWdvbmlzdCBsw6BtIHRoYW0gY2hp4bq/dSwgT1IgY+G7p2EgRHVhbCB0cmlnZ2VyID0gMS4wMTYsIHR1eSBuaGnDqm4gS1RDOTUlIGNo4bupYSBnacOhIHRy4buLIDEgdsOgIHAgdmFsdWUgPiAwLjA1LCBuw6puIGtow7RuZyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqLg0KDQpUxrDGoW5nIHThu7EsIE9SIGPhu6dhIG5ow7NtIGhDRyBzbyB24bubaSBEdWFsIHRyaWdnZXIgPSAwLjk2IDwgMSwgdHV5IG5oacOqbiBjxaluZyBraMO0bmcgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqi4NCg0KDQpUYSBjw7JuIGPDsyB0aOG7gyDGsOG7m2MgdMOtbmggaGnhu4d1IOG7qW5nIGLhurFuZyBSaXNrLXJhdGlvIChSUiksIGNo4buJIGPhuqduIHRoYXkgaMOgbSB0cmFuc2Zvcm1fcHJlID0gImxucmF0aW9hdmciLg0KDQpgYGB7cn0NCnJyID0gYXZnX2NvbXBhcmlzb25zKA0KICBiaV9tb2QsDQogIHdoYXQgPSAibXUiLA0KICB2YXJpYWJsZXMgPSBsaXN0KFRyaWdnZXIgPSAncGFpcndpc2UnKSwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbnJhdGlvYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSBleHApJT4lDQogIGRwbHlyOjpzZWxlY3QoLWMoMSkpDQoNCnJyICU+JSBrbml0cjo6a2FibGUoKQ0KYGBgDQoNClRhIGRp4buFbiBnaeG6o2kga+G6v3QgcXXhuqMgUlIgdMawxqFuZyB04buxIG5oxrAgdHLDqm4sIGtow7RuZyBjw7Mgc+G7sSBraMOhYyBiaeG7h3QgZ2nhu69hIER1YWwgdHJpZ2dlciB2w6AgbmjDs20gdGhhbSBjaGnhur91IEFnb25pc3QgaG/hurdjIGhDRy4NCg0KTmdvw6BpIHJhLCB0YSBjw7MgdGjhu4MgxJFpIHhhIGjGoW4ga2hpIMaw4bubYyBsxrDhu6NuZyBoaeG7h3Ug4bupbmcgKE9SIGhv4bq3YyBSUikgY+G7p2EgRHVhbCB0cmlnZ2VyIHRyb25nIG5o4buvbmcgxJFp4buBdSBraeG7h24ga2jDoWMgbmhhdSBj4bunYSBoaeG7h3AgYmnhur9uLCB0aMOtIGThu6UgQUZDID0gMiw1LDEwIHbDoCAyMCBuaMawIHNhdToNCg0KYGBge3J9DQpvciA9IGF2Z19jb21wYXJpc29ucygNCiAgYmlfbW9kLA0KICB3aGF0ID0gIm11IiwNCiAgYnkgPSAiQUZDIiwNCiAgdmFyaWFibGVzID0gbGlzdChUcmlnZ2VyID0gJ3BhaXJ3aXNlJyksDQogIG5ld2RhdGEgPSBkYXRhZ3JpZChBRkMgPSBjKDIsNSwxMCwyMCksDQogICAgICAgICAgICAgICAgICAgICBncmlkX3R5cGUgPSAnY291bnRlcmZhY3R1YWwnKSwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbm9yYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSAiZXhwIikNCg0Kb3IgJT4lIGtuaXRyOjprYWJsZSgpDQpgYGANCg0KS+G6v3QgcXXhuqMgbsOgeSBraMOhIHRow7ogduG7iywgdsOsIG7DsyBjaG8gdGjhuqV5IHLhurFuZyBraGkgQUZDIDw9IDUsIE9SIGPhu6dhIER1YWwgdHJpZ2dlciBzbyB24bubaSBBZ29uaXN0IGzhuqduIGzGsOG7o3QgbMOgIDEuMjcgdsOgIDEuMjEgdsOgIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouIA0KDQpUYSBjw7MgdGjhu4MgdHLDrG5oIGLDoHkgdHLhu7FjIHF1YW4gduG7gSBoaeG7h3Ug4bupbmcgY+G7p2Ega+G7uSB0aHXhuq10IHRyaWdnZXIgxJHhu5FpIHbhu5tpIHThu7cgbOG7hyB04bqhbyBibGFzdG9jeXRlLCBk4buxYSB2w6BvIG3DtCBow6xuaCBCaW5vbWlhbCBuaMawIHNhdToNCg0KYGBge3J9DQpwcmVkcyA9IHByZWRpY3Rpb25zKGJpX21vZCwgDQogICAgICAgICAgICAgICAgICAgIHdoYXQgPSAibXUiLCANCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFncmlkKFRyaWdnZXIgPSBjKCdkJywnYScsJ2gnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRfdHlwZSA9ICJjb3VudGVyZmFjdHVhbCIpLA0KICAgICAgICAgICAgICAgICAgICBuZXdfZGF0YSA9IGRmKQ0KDQpwcmVkcyU+JWdncGxvdCgpKw0KICBzdGF0X2hhbGZleWUoYWxwaGEgPSAwLjQsIA0KICAgICAgICAgICAgICAgYWVzKHggPSBUcmlnZ2VyLCANCiAgICAgICAgICAgICAgICAgICB5ID0gZXN0aW1hdGUsDQogICAgICAgICAgICAgICAgICAgZmlsbCA9IFRyaWdnZXIpLA0KICAgICAgICAgICAgICAgLndpZHRoID0gYygwLjc1LCAwLjk1KSwNCiAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IFQpKw0KICBzdGF0X2xpbmVyaWJib24oYWVzKHkgPSBlc3RpbWF0ZSwgeCA9IFRyaWdnZXIpLCANCiAgICAgICAgICAgICAgICAgIGZpbGwgPSAnZ29sZCcsDQogICAgICAgICAgICAgICAgICAud2lkdGggPSBjKC45NSwgLjc1LCAuNTApLCANCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMS84LA0KICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArDQogIGxhYnMoeT0iUmF0ZSBvZiBibGFzdG9jeXRlcyIsIHggPSAiVHJpZ2dlciIpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHMsIG5hbWUgPSAiVHJpZ2dlciIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwwLjQsMC4wNSkpKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNClRhIGPDsyB0aOG7gyB0csOsbmggYsOgeSBjw7luZyBr4bq/dCBxdeG6oyB0csOqbiBuaMawbmcgY2hvIHJpw6puZyA2IMSRaeG7gXUga2nhu4duIGtow6FjIG5oYXUgduG7gSBnacOhIHRy4buLIEFGQyA9IDEsNSwxMCwxNSwyMCB2w6AgMzANCg0KYGBge3J9DQpwcmVkcyA9IHByZWRpY3Rpb25zKG1vZGVsID0gYmlfbW9kLA0KICAgICAgICAgICAgICAgICAgICB3aGF0ID0gIm11IiwNCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFncmlkKEFGQyA9IGMoMSw1LDEwLDE1LDIwLDMwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRfdHlwZSA9ICJjb3VudGVyZmFjdHVhbCIpKQ0KDQpwcmVkcyU+JWdncGxvdCgpKw0KICBzdGF0X2hhbGZleWUoYWxwaGEgPSAwLjQsIA0KICAgICAgICAgICAgICAgYWVzKHggPSBUcmlnZ2VyLCANCiAgICAgICAgICAgICAgICAgICB5ID0gZXN0aW1hdGUsDQogICAgICAgICAgICAgICAgICAgZmlsbCA9IFRyaWdnZXIpLA0KICAgICAgICAgICAgICAgLndpZHRoID0gYygwLjc1LCAwLjk1KSwNCiAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IFQpKw0KICBzdGF0X2xpbmVyaWJib24oYWVzKHkgPSBlc3RpbWF0ZSwgeCA9IFRyaWdnZXIpLCANCiAgICAgICAgICAgICAgICAgIGZpbGwgPSAnZ29sZCcsDQogICAgICAgICAgICAgICAgICAud2lkdGggPSBjKC45NSwgLjc1LCAuNTApLCANCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMS84LA0KICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArDQogIGxhYnMoeT0iUmF0ZSBvZiBibGFzdG9jeXRlcyIsIHggPSAiVHJpZ2dlciIpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHMsIG5hbWUgPSAiVHJpZ2dlciIpKw0KICBmYWNldF93cmFwKH5BRkMsIHNjYWxlcyA9ICJmcmVlIikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIERp4buFbiBnaeG6o2kga+G6v3QgcXXhuqMgY+G7p2EgcGjDom4gdMOtY2gNCg0KS+G6v3QgcXXhuqMgcGjDom4gdMOtY2ggaOG7k2kgcXV5IGNobyB0aOG6pXkg4bufIGPhuqVwIMSR4buZIHF14bqnbiB0aOG7gywga+G7uSB0aHXhuq10IGR1YWwgdHJpZ2dlciBjw7MgaGnhu4d1IHF14bqjIHTGsMahbmcgxJHGsMahbmcgduG7m2kgMiBrw70gdGh14bqtdCBBZ29uaXN0IHbDoCBoQ0cgduG7gSB04bu3IGzhu4cgdOG6oW8gYmxhc3RvY3l0ZXMuIFR1eSBuaGnDqm4sIOG7nyBuaOG7r25nIMSR4buRaSB0xrDhu6NuZyBjw7MgQUZDIDw1LCBoaeG7h3UgcXXhuqMgdOG6oW8gYmxhc3RvY3l0ZSBj4bunYSBEdWFsIHRyaWdnZXIgY2FvIGjGoW4gbeG7mXQgY8OhY2ggY8OzIMO9IG5naMSpYSBzbyB24bubaSBBZ29uaXN0IChPUiA9IDEuMjcsIEtUQzk1JTogMS4wNSAtIDEuNTQsIHA9MC4wMSku