1 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.

2 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.

3 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()
Trigger Age AFC oocytes blast p1
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

4 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()
Trigger n mean SD p5 p95
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.

5 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:

\[log⁡it(\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.

6 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()
term contrast estimate std.error p.value conf.low conf.high
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()
term contrast estimate p.value conf.low conf.high
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()
term contrast estimate p.value conf.low conf.high
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()
type term contrast AFC estimate p.value conf.low conf.high
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()

7 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