1 Giới thiệu

Ở chương này, chúng ta sẽ khảo sát thành quả được quan tâm nhất trong nghiên cứu về hỗ trợ sinh sản, đó là sự thụ thai (thai sinh hóa, lâm sàng), sự tiếp diễn thành công của thai kì (thai diễn tiến) và sự sinh nở. Trong y văn, những kết cục này thường được khảo sát như một giá trị mang tính nhị phân (Có hoặc không), hoặc tỷ lệ thành công trên tổng số cá thể bệnh nhân. Tuy nhiên, bản chất của loại kết cục này có thể được nhìn nhận như một xác suất hay khả năng thành công và cũng có tính kế thừa như một phần trong chuỗi kết quả của tiến trình thụ tinh nhân tạo. Vì vậy, ta có thể áp dụng quy luật phân phối nhị thức (binomial) để khảo sát chúng.

Thí dụ, kết quả thai diễn tiến thường được ghi nhận bằng 2 giá trị 0 (không) hoặc 1 (có) trong dữ liệu, nhưng bản chất của thai diễn tiến có thể xem như xác suất thành công \(p\) của 1 thử nghiệm chuyển phôi. Chính vì trên thực tế, số lượng phôi được chuyển rất thấp, chỉ giới hạn ở 1 đến 2 phôi cho mỗi chu kì, nên kết quả quan sát được mới biểu hiện thành giá trị 0 hoặc 1.

Trong chương này, ta sẽ tìm hiểu về phân tích hồi quy logistic và trường hợp tổng quát của nó - một mô hình hồi quy GLM với phân phối Binomial, cho phép suy diễn thống kê cho kết quả nhị phân hoặc xác suất thành công của thử nghiệm.

Tình huống minh họa là một thử nghiệm lâm sàng với mục tiêu so sánh hiệu quả của phác đồ PPOS so với phác đồ tham chiếu là GnRH_ant đối với khả năng đạt được thai diễn tiến (ongoing pregnancy). Trong thí nghiệm này, phác đồ GnRH_ant được áp dụng cho nhóm đối chứng gồm 107 phụ nữ hiếm muộn, và phác đồ PPOS áp dụng cho nhóm can thiệp gồm 99 trường hợp. Sau khi thu hoạch noãn, thụ tinh và trữ phôi, người ta thực hiện từ 1 đến 3 lần chuyển phôi, mỗi lần 1-2 phôi và ghi nhận kết quả thai diễn tiến cộng dồn (nếu một trường hợp không thành công ở lần chuyển phôi đầu tiên, ta sẽ lặp lại quy trình chuyển phôi lần 2, hoặc lần 3). Thử nghiệm kết thúc ở lần thứ 3.

Kết cục sẽ được khảo sát dưới 3 hình thức:

  1. Giá trị nhị phân: thành công hay thất bại tuyệt đối : có đạt được thai diễn tiến hay không (cho tối đa 3 lần): 0 = Không, 1 = Có (bất kể song thai, đơn thai)

  2. Tần suất: số lượng thai diễn tiến cho mỗi cá thể (giá trị = 0 (không đạt được), 1 = đơn thai diễn tiến, 2 = song thai diễn tiến).

  3. Tỷ lệ đạt thai diễn tiến = số thai diễn tiến chia cho tổng số phôi được chuyển. Thí dụ: Lần 1 chuyển 2 phôi, kết quả không đạt, lần 2 chuyển 2 phôi và đạt đơn thai, thì tỷ lệ thành công = \(1/(2+2) = 0.25\)

Lưu ý rằng các trường hợp: sẫy thai, thai ngoài tử cung… xem như thất bại.

2 Kế hoạch phân tích

Xác suất đạt thai diễn tiến cộng dồn khi áp dụng phác đồ PPOS, so với phương pháp tham chiếu là GnRH_ant sẽ được ước lượng bằng marginal Odds-ratio (OR) và risk-ratio (RR), có hiệu chỉnh cho Tuổi, AFC và độ dày nội mạc trung bình ngày chuyển phôi, dựa vào một mô hình hồi quy logistic. 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 patchwork để ghép nhiều biểu đồ ggplot2 với nhau.

  • 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)
library(patchwork)

Đầu tiên, ta tải dữ liệu từ file ‘PPOS_OP.csv’ vào dataframe df, sau đó ta

df = read.csv('PPOS_OP.csv', sep = ';', 
              dec = ',', 
              fileEncoding = 'UTF-8-BOM')%>%
  na.omit()

df$Protocol = factor(df$Protocol)

df%>%dplyr::sample_frac(0.3)%>%head()%>%knitr::kable()
Age Protocol AFC n_ET OP_cum Thickness OP_bin OP_rate
34 PPOS 11 2 1 9.200000 1 0.5
27 PPOS 13 2 2 12.233333 1 1.0
22 GnRHa 17 2 1 13.900000 1 0.5
32 GnRHa 9 2 1 9.133333 1 0.5
34 GnRHa 11 2 0 14.300000 0 0.0
34 PPOS 12 1 1 13.600000 1 1.0

Trong dữ liệu này:

Age = Tuổi bệnh nhân,

AFC = Dự trữ buồng trứng cơ bản;

Thickness = độ dày nội mạc tử cung;

n_ET: tổng số phôi đã chuyển trong tiến trình nghiên cứu;

OP_cum = tần suất thai diễn tiến cộng dồn (giá trị cao nhất = 2, thấp nhất = 0);

OP_bin = kết quả thành công/thất bại tuyệt đối (2 giá trị 0,1)

OP_rate = tỷ lệ thai diễn tiến = OP_cum / n_ET

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 để thực hiện thống kê mô tả cho những hình thức khác nhau của kết cục thai diễn tiến cộng dồn:

  1. Khảo sát tần suất thai diễn tiến (biến OP_cum)

Đây là một biến số rời rạc và chỉ có thể có 3 giá trị: 0, 1 hoặc 2. Kết quả mô tả như sau:

getmode <- function(v) {
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}

df%>%group_by(Protocol)%>%
  summarize(n = n(),
            Sum = sum(OP_cum),
            mean = sprintf("%0.2f", mean(OP_cum)),
            Median = sprintf("%0.2f", median(OP_cum)),
            Mode = sprintf("%0.2f", getmode(OP_cum)),
            SD = sprintf("%0.2f", sd(OP_cum)),
            p5 = sprintf("%0.2f", quantile(OP_cum, 0.05)),
            p95 = sprintf("%0.2f", quantile(OP_cum, 0.95)),
            )%>%knitr::kable()
Protocol n Sum mean Median Mode SD p5 p95
GnRHa 107 87 0.81 1.00 1.00 0.70 0.00 2.00
PPOS 99 102 1.03 1.00 1.00 0.66 0.00 2.00

Theo kết quả này, nếu xét về số lượng tuyệt đối, thì dường như giữa 2 phác đồ PPOS và GnRH không có sự khác biệt nào cả: Mode = 1 cho thấy kết quả thường gặp nhất cho cả 2 phác đồ là đạt ít nhất 1 thai diễn tiến. Xét bình quân thì nhóm PPOS có vẻ ưu thế hơn: số thai diễn tiến trung bình là 1.03 so với 0.81; cả 2 đều có độ phân tán cao tương đương (SD khoảng 0.7).

  1. Nếu xét về tỷ lệ thai diễn tiến trên tổng số phôi chuyển:
df%>%group_by(Protocol)%>%
  summarize(n = n(),
            Mean = sprintf("%0.3f", mean(OP_rate)),
            Median = sprintf("%0.3f", median(OP_rate)),
            SD = sprintf("%0.3f", sd(OP_rate)),
            p5 = sprintf("%0.3f", quantile(OP_rate, 0.05)),
            p95 = sprintf("%0.3f", quantile(OP_rate, 0.95)),
            )%>%knitr::kable()
Protocol n Mean Median SD p5 p95
GnRHa 107 0.370 0.500 0.355 0.000 1.000
PPOS 99 0.457 0.500 0.343 0.000 1.000

Kết quả này cho thấy tỳ lệ thai diễn tiến trung bình có vẻ ưu thế hơn ở nhóm PPOS so với GnRHant: 0.46 so với 0.37 (nói cách khác, xác suất thụ thai thành công cho mỗi phôi chuyển có vẻ cao hơn ở phác đồ PPOS); Ta có thể ước tính Risk ratio (RR) giữa 2 phác đồ: \(RR = 0.457/0.37 = 1.235\). Tuy nhiên giá trị trung vị lại tương đương (0.5). Nói cách khác, nếu cứ chuyển phôi 2 lần thì dự kiến ít nhất sẽ có 1 lần đạt thành công.

  1. Tỷ số xác suất thành công/thất bại (Odds)

Ta có thể ước lượng kết quả thai diễn tiến theo một cách khác, bằng tỷ số Odds

\[Odds = \frac{p_1}{p_0} = \frac{p_1}{(1 - p_1)}\]

Với \(p_1\) là xác suất thành công, \(p_0\) là xác suất thất bại.

df%>%mutate(Odds = OP_rate/(1-OP_rate))->odd_df

odd_df$Odds[!is.finite(odd_df$Odds)] <- NA

odd_df%>%
  group_by(Protocol)%>%
  summarize(n = n(),
            Mean = sprintf("%0.3f", mean(Odds, na.rm=TRUE)),
            Median = sprintf("%0.3f", median(Odds,na.rm=TRUE)),
            SD = sprintf("%0.3f", sd(Odds, na.rm=TRUE)),
            p5 = sprintf("%0.3f", quantile(Odds, 0.05, na.rm=TRUE)),
            p95 = sprintf("%0.3f", quantile(Odds, 0.95,na.rm=TRUE)),
            )%>%knitr::kable()
Protocol n Mean Median SD p5 p95
GnRHa 107 0.462 0.333 0.470 0.000 1.000
PPOS 99 0.556 0.500 0.426 0.000 1.000

Theo kết quả này, Odds của nhóm PPOS cao hơn Odds của nhóm GnRHant (0.556 so với 0.462), nói cách khác Odds ratio giữa PPOS và GnRHa : \(OR = 0.556/0.462 = 1.203\)

  1. Nếu xét giá trị thành công hay thất bại tuyệt đối (biến nhị giá OP_bin)

Khi thực hiện phân tích này, đơn vị quan sát là cá thể bệnh nhân thay vì đơn vị phôi. Cách khảo sát này sẽ cho ra một kết quả khác

df%>%group_by(Protocol)%>%
  summarize(n = n(),
            Rate = mean(OP_bin),
            Freq = sum(OP_bin),
            Odds = Rate/(1-Rate),
            )%>%knitr::kable()
Protocol n Rate Freq Odds
GnRHa 107 0.6448598 69 1.815789
PPOS 99 0.7979798 79 3.950000

Dựa vào kết quả này, tỷ số xác suất thành công giữa 2 phác đồ : \(RR = 0.798/0.645 = 1.237\) và Odds-ratio giữa chúng \(OR = 3.95/1.815 = 2.176\). Ở đây ta thấy một điểm thú vị là khi chọn đơn vị khảo sát là bệnh nhân, kết quả OR và RR có khuynh hướng cao hơn so với khi xét đơn vị khảo sát là phôi.

Ta lần lượt khảo sát trực quan 3 hình thức khảo sát kết quả thai diễn tiến trong 3 biểu đồ sau

pals = c("#0faaf2","#f20f4f")

p1 = df %>% ggplot(aes(y = OP_cum, 
                  x = Protocol, 
                  fill= Protocol)) + 
  geom_jitter(aes(fill = Protocol),
              shape = 21,
              alpha = 0.8,
              width = 0.1,
              height = 0.2,
              show.legend = T)+
  labs(y="Cummulated OP", x = "Protocol") + 
  scale_fill_manual(values = pals, name = "Protocol") +
  scale_y_continuous(breaks = c(0,1,2,3))+
  theme_bw(10) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

p2 = df %>% ggplot(aes(x = OP_rate, 
                  y = Protocol, 
                  fill= Protocol)) + 
  geom_density_ridges(aes(fill = Protocol),
                      stat = "binline",
                      scale = 0.5, 
                      bins = 30,
                      alpha = 0.8)+
  labs(x="Cummulated OP rate", y = "Protocol") + 
  scale_fill_manual(values = pals, name = "Protocol") +
  coord_flip()+
  theme_bw(10)

p3 = df%>%
  group_by(Protocol)%>%
  summarise(Success = mean(OP_bin),
            Failure = 1 - Success)%>%
  gather(c(2,3),key = "OP", value = "Rate")%>%
  ggplot(aes(x = Protocol, y = Rate))+
  geom_bar(aes(fill = OP), stat = "identity")+
  scale_fill_manual(values = c("#c0bec2","#f03a79"), name = "OP Outcome") +
  theme_bw(10)


p3 + p1/p2

Chú thích: Hình bên trái là biểu đồ cột chồng trình bày phân bố tỷ lệ Thành công/Thất bại (trục Y) giữa 2 phác đồ (trục X), hình bên phải gồm 2 biểu đồ: góc trên là biểu đồ tán xạ so sánh tần suất thai diễn tiến (trục Y) giữa 2 phác đồ (trục X), góc dưới là biểu đồ tần suất mô tả tỷ lệ thai diễn tiến cộng dồn (trục Y) giữa 2 phác đồ (trục X).

Ngoài 2 phác đồ, trong dữ liệu còn có một số hiệp biến khác như Tuổi, AFC và độ dày nội mạc có bản chất là biến số liên tục. Ta muốn khảo sát trực quan mối liên hệ giữa những hiệp biến này và xác suất đạt thai diễn tiến; ta có thể thực hiện theo 3 cách:

  1. Liên hệ biến X và giá trị 0,1 của biến OP_bin, thông qua đồ thị của mô hình GLM với phân phối binomial (đồ thị hàm logistic)

  2. Liên hệ biến X và giá trị tỷ lệ OP_rate, thông qua 1 mô hình hồi quy GLM với phân phối quasibinomial;

  3. Liên hệ biến X và Odds, thông qua một mô hình hồi quy tuyến tính

p4 = df%>%gather(c(1,3,6), 
            key = "Factor", 
            value = "value")%>%
  ggplot(aes(x = value, y = OP_bin))+
  geom_smooth(aes(fill = Protocol, 
                  col = Protocol),
              method = "glm", 
              method.args = list(family = "binomial"),
              show.legend = F)+
  scale_y_continuous(limits = c(0,1))+
  scale_fill_manual(values = pals, name = "Protocol") +
  scale_color_manual(values = pals, name = "Protocol") +
  facet_wrap(~Factor, ncol=1, scales = "free_x")+
  labs(y="Binary OP", x = NULL) + 
  theme_bw(10)

p5 = df%>%gather(c(1,3,6), 
            key = "Factor", 
            value = "value")%>%
  ggplot(aes(x = value, y = OP_rate))+
  geom_smooth(aes(fill = Protocol, 
                  col = Protocol),
              method = "glm", 
              method.args = list(family = "quasibinomial"))+
  scale_y_continuous(limits = c(0,1))+
  scale_fill_manual(values = pals, name = "Protocol") +
  scale_color_manual(values = pals, name = "Protocol") +
  facet_wrap(~Factor, ncol=1, scales = "free_x")+
  labs(y="OP rate", x = NULL) + 
  theme_bw(10)

p6 = df%>%mutate(odd = OP_rate/(1-OP_rate))%>%
  gather(c(1,3,6),
            key = "Factor", 
            value = "value")%>%
  ggplot(aes(x = value, y = odd))+
  geom_smooth(aes(fill = Protocol, 
                  col = Protocol),
              method = "glm",
              show.legend = F)+
  scale_fill_manual(values = pals, name = "Protocol") +
  scale_color_manual(values = pals, name = "Protocol") +
  facet_wrap(~Factor, ncol=1, scales = "free_x")+
  labs(y="Odds = p1/p0", x = NULL) + 
  theme_bw(10)
                
p4+p6+p5

Chú thích: Hình trên gồm 3 cột, từ trái sang phải lần lượt trình bày: (1) Liên hệ giữa giá trị AFC, Age, Thickness (3 hàng, trục X) và khả năng đạt thai diễn tiến (trục Y); (2) Liên hệ giữa giá trị AFC, Age, Thickness (3 hàng, trục X) và Odds (tỉ số giữa xác suất thành công/thất bại); và (3) Liên hệ giữa giá trị AFC, Age, Thickness (3 hàng, trục X) và tỷ lệ thai diễn tiến trên tổng số phôi chuyển (trục Y). Dữ liệu được phân thành 2 nhóm: phác đồ PPOS (màu hồng) và GnRha (màu xanh).

Hình ảnh này gợi ý một số giả thuyết như sau:

  1. AFC có hiệu ứng tương tác giữa AFC và loại phác đồ, vì 2 đồ thị các hàm tuyến tính/logistic của 2 nhóm cắt nhau, với hiệu ứng AFC khác biệt ở mỗi nhóm.

  2. Tuổi có hiệu ứng độc lập và đồng nhất ở 2 phác đồ, khả năng thai diễn tiến tương quan nghịch với tuổi (giảm dần ở phụ nữ lớn tuổi);

  3. Độ dày nội mạc tử cung có hiệu ứng độc lập với phác đồ, và đồng nhất giữa 2 nhóm phác đồ, hiệu ứng này rất yếu và có khuynh hướng tương quan thuận với khả năng thai diễn tiến.

5 Lý thuyết về mô hình hồi quy logistic

Cho bài toán thống kê hiện thời, ta cần một phương tiện cho phép liên kết giá trị của một biến độc lập (yếu tố tiên lượng) \(X\) và một xác suất \(Y = p \in [0,1]\). Công cụ đó là mô hình logistic.

Mô hình hồi quy logistic có một lịch sử lâu đời (khoảng 78 năm), trước cả sự ra đời của lý thuyết về mô hình tuyến tính tổng quát GLM. Câu chuyện này khá dài, và sự khởi đầu của nó không liên quan gì đến xác suất và thống kê. Chúng ta sẽ khởi hành xa hơn từ chỗ nhận ra trong thế giới tự nhiên luôn hiện diện một quy luật tiến triển đặc biệt gồm 3 giai đoạn: 1) Giai đoạn sớm với sự tăng trưởng lũy tiến, xấp xỉ hàm mũ (exponential); 2) Tiếp theo là giai đoạn bão hòa: tốc độ phát triển chậm lại, tuyến tính; 3) Cuối cùng là giai đoạn ổn định và trưởng thành: sự phát triển ngừng lại. Khi biểu diễn tiến trình này theo thời gian, sẽ tạo ra một đường cong hình chữ S (sigmoid curve).

Ta có thể quan sát thấy quy luật này ở mọi lĩnh vực: tăng trưởng dân số/kinh tế, chu trình chuyển hóa sinh học, phản ứng hóa học. Trong Sản Phụ khoa, ta có thể nhìn thấy nó trong diễn tiến các hormone sinh dục, sự tăng trưởng của bào thai trong tử cung người mẹ, liên hệ liều / đáp ứng trong dược lực học…

Từ thế kỉ 18-19, những nhà toán học đã khảo sát quy luật này và lập ra các hàm toán học cho phép tái hiện đường cong hình chữ S, gọi là các hàm sigmoid, thí dụ hàm hyperbolic tangent của Jean Saurry năm 1774.

Trong số các hàm sigmoid, ta có hàm logistic là nhân vật chính trong câu chuyện này. Hàm logistic do nhà toán học người Pháp Pierre François Verhulst thiết lập vào năm 1845, với công dụng nguyên thủy nhằm khảo sát quy luật tăng trưởng dân số trong một quần thể. Công thức của hàm logistic được trình bày như sau.

\[y = \frac{e^{x}}{1 + e^{x}} = \frac{1}{1 + e^{-x}} = \frac{1}{2} + \frac{1}{2} tanh(\frac{x}{2})\]

Hình: đồ thị hàm logistic. Từ giá trị đầu vào là số thực \(x\) bất kì, hàm logistic xuất kết quả là một giá trị trong khoảng (0,1).

Một cách tình cờ, đặc tính này của hàm logistic dẫn đến một ứng dụng quan trọng trong lĩnh vực thống kê: nó giải quyết nhu cầu liên kết giá trị một tập biến \(X\) và xác suất quan sát được giá trị \(Y=1\) (Y là một biến nhị giá).

Mô hình logistic cổ điển được định nghĩa trực tiếp dựa vào hàm logistic, bằng cách thay đối số \(x\) bằng một đại lượng \(g\) như sau:

\[Pr(Y=1|X) \sim logistic(X) = \frac{1}{1 + e^{-g}}\]

Với \(g\) là một biểu thức (hàm) tuyến tính được mô hình hóa theo tập biến \(X\)

\[g = \beta_0 + \beta_1X_1 + \beta_2X_2 + ... + \beta_jX_j\] Tuy nhiên, phiên bản thực dụng của mô hình hồi quy logistic chỉ được định hình vào năm 1944, khi nhà thống kê Joseph Berkson thiết lập một khái niệm quan trọng là Log odds và dùng nó làm thang đo cho bài toán ước lượng biến nhị giá.

Như ta biết, Odds là tỉ số giữa 2 xác suất. Với \(p\) là xác suất thành công (\(Y=1\)) và \(1-p\) là xác suất thất bại (\(Y=0\)), ta có:

\[Odds = \frac{p}{1-p}\]

Từ đó, ta có hàm logit là một công cụ khác cho phép liên kết trực tiếp tập biến \(X\) và giá trị xác suất \(Y=p\). Hàm logit chính là sự áp dụng hàm logarit tự nhiên cho Odds:

\[logit(p) = log(\frac{p}{1-p})\]

Ta dễ dàng nhận thấy mối liên hệ nghịch đảo giữa 2 hàm logit và hàm logistic cổ điển:

Từ đó, Berkson đã chuẩn hóa hồi quy logistic thành một phương pháp thống kê quy ước, sử dụng hàm logit thay vì logistic, và dùng log(odds) làm thang đo, đơn vị cho biến kết quả \(Y\) trong mô hình. Sau đó, David Cox (1958) hoàn thiện kỹ thuật ước lượng tham số trong mô hình logistic.

Sau cùng, vào năm 1972 Nelder và Wedderburn đã hợp nhất mô hình logistic vào hệ thống mô hình hồi quy tuyến tính tổng quát (GLM), mô hình logistic là một thể đặc biệt của mô hình GLM, ước lượng giá trị của \(\mu\) là trung bình của biến kết quả \(Y\) theo quy luật phân phối Binomial và 2 tham số \(BI(n,\mu)\)

\[Pr(Y=y|n,\mu) = \frac{n!}{y!(n-y)!}\mu^{y}(1-p)^{n-y}\]

Ta ước lượng giá trị của \(\mu\) bằng một mô hình tuyến tính với hàm liên kết logit:

\[logit(\mu) = log(\frac{\mu}{1-\mu}) \sim \beta_0 + \beta_1X_1 + \beta_2X_2 + ... + \beta_jX_j \] Ta thấy, định nghĩa mô hình hồi quy logistic theo lý thuyết GLM và hàm logit dễ hiểu và đơn giản hơn nhiều so với định nghĩa nguyên thủy dựa vào hàm logistic. Nhưng vì lý do lịch sử, ta vẫn dùng tên gọi “logistic regression” cho loại mô hình này, nhưng ta hiểu bản chất của nó chỉ đơn giản là một mô hình GLM với phân phối Binomial hoặc Bernoulli (trường hợp đặc biệt khi \(n=1\))

6 Bước 2A: Mô hình hồi quy Logistic cho biến kết quả nhị giá

Ta sẽ lần lượt phân tích 2 mô hình logistic khác nhau cho 2 biến kết quả: 1) kết quả nhị giá (thành công hoặc thất bại tuyệt đối về thai diễn tiến trong thí nghiệm, biến OP_bin) và 2) tỷ lệ thai diễn tiến trên tổng số phôi chuyển (biến OP_rate).

Cho kết quả thứ nhất, mô hình áp dụng một trường hợp đặc biệt của phân phối Binomial với \(n=1\), với đơn vị quan sát là cá thể bệnh nhân. Mục tiêu của mô hình là ước lượng xác suất quan sát được giá trị \(OP\_bin = 1\) cho mỗi cá thể.

Mô hình có công thức: OP_bin ~ Protocol*AFC + Thickness + Age; family = “binomial” (hàm liên kết mặc định là logit).

Nội dung của mô hình như sau:

log_mod = glm(OP_bin ~  Protocol*AFC + Thickness + Age,
                data = df,
                family = "binomial")

summary(log_mod)
## 
## Call:
## glm(formula = OP_bin ~ Protocol * AFC + Thickness + Age, family = "binomial", 
##     data = df)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.2355  -0.9405   0.5561   0.7987   1.6910  
## 
## Coefficients:
##                  Estimate Std. Error z value Pr(>|z|)    
## (Intercept)       2.28220    1.61373   1.414 0.157292    
## ProtocolPPOS      3.56952    1.02745   3.474 0.000512 ***
## AFC               0.14222    0.05030   2.828 0.004690 ** 
## Thickness         0.10929    0.07437   1.470 0.141679    
## Age              -0.14522    0.04414  -3.290 0.001001 ** 
## ProtocolPPOS:AFC -0.22953    0.07846  -2.925 0.003439 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 244.90  on 205  degrees of freedom
## Residual deviance: 215.36  on 200  degrees of freedom
## AIC: 227.36
## 
## Number of Fisher Scoring iterations: 4

Diễn giải nội dung kết quả thô của mô hình:

Do sử dụng hàm liên kết là logit, giá trị ước lượng của mô hình hiện thời là \(logit(\mu)\) hay \(log(\frac{\mu}{1-\mu})\) hay \(log(Odds)\), với \(\mu\) là xác suất đạt thai diễn tiến trung bình (cho mổi cá thể). Tất cả hệ số hồi quy đang ở thang đo log(odds). Ta có thể chuyển về thang đo xác suất bằng hàm plogis, hoặc về thang đo Odds bằng hàm exponential.

\(Intercept = 2.28220\) tương ứng với log(odd) của xác suất thai diển tiến ở phác đồ tham chiếu (GnRH_ant), trong điều kiện giá trị Age, AFC và Thickness giữ cố định ở mức trung bình của quần thể.

\(ProtocolPPOS = 3.56952\) tương ứng với sự thay đổi log(odd) của xác suất thai diễn tiến ở phác đồ PPOS so với nhóm đối chứng (GnRH_ant), trong điều kiện giá trị Age, AFC và Thickness không đổi.

Các hệ số hối quy cho AFC,Age và Thickness lần lượt ứng với sự thay đổi của log(odd) xác suất thai diễn tiến khi AFC, Age hoặc Thickness tăng 1 đơn vị.

Vì mô hình có xét hiệu ứng tương tác giữa phác đồ PPOS và AFC, ta có thêm hệ số hồi quy ProtocolPPOS:AFC; với ý nghĩa đo lường sự thay đổi của log(odd) của xác suất thai diễn tiến khi AFC tăng 1 đơn vị và khi sử dụng phác đồ PPOS.

Một cách đơn giản, ta có thể xét dấu của các hệ số hồi quy này để hình dung về hiệu ứng mà các biến gây ra cho xác suất của thai diễn tiến. Một hệ số hồi quy > 0 cho thấy mối tương quan thuận (làm tăng), ngược lại một hệ số hồi quy <0 cho thấy tương quan nghịch (làm giảm).

Pr(>|z|) trình bày giá trị p của kiểm định Wald, nhằm phủ nhận giả thuyết vô hiệu là một biến không gây ra hiệu ứng nào cả (không có liên hệ) đối với xac suất OP (hệ số hồi quy = 0).

Tuy nhiên, rất khó để thực hiện suy diễn thống kê theo cách này, nên ta sẽ theo con đường ước tính marginal effects (hiệu ứng cận biên) cho các yếu tố.

Giả định ta muốn khảo sát hiệu ứng của một biến định tính có 2 bậc giá trị (phác đồ PPOS hoặc GnRH), mô hình sẽ ước lượng được 2 giá trị xác suất thai diễn tiến trung bình cho 2 nhóm này là \(\mu_{PPOS}\)\(\mu_{GnRH}\)

Từ đây, ta có thể ước tính 3 loại marginal effects gồm:

  1. Marginal risk difference (RD)

Trị số RD đo lường trực tiếp sự thay đổi/khác biệt/tương phản về xác suất giữa PPOS và phác đồ tham chiếu

\[RD = \mu_{PPOS} - \mu_{GnRHant}\] RD cho biết khi dùng phác đồ PPOS, xác suất đạt thay diễn tiến sẽ thay đổi bao nhiêu so với phác đồ tham chiếu. Vì bản chất của RD là hiệu số giữa 2 xác suất, nó chỉ có thể dao động trong khoảng \([-1,1]\), RD = 0 cho thấy không có khác biệt (tính tương đương), RD < 0 hoặc > 0 cho phép xác định hiệu ứng của phác đồ nào cao hơn/thấp hơn.

rd = avg_comparisons(
  log_mod,
  variables = "Protocol")%>%
  dplyr::select(-c(1,2,5,6))%>%
  mutate(contrast = "RD")

rd %>% knitr::kable()
contrast estimate p.value conf.low conf.high
RD 0.1485632 0.0095652 0.0361896 0.2609369

Kết quả cho thấy: theo ước tính trung bình, nhóm bệnh nhân dùng phác đồ PPOS có xác suất thai diễn tiến tăng 0.148 (nói cách khác; tỷ lệ thai diễn tiến tăng 14.8%) so với phác đồ GnRH_ant, sự gia tăng này có ý nghĩa thống kê (khoảng tin cậy không chứa giá trị 0, p = 0.036).

  1. Marginal Odds ratio (OR)

Trong phần trên, ta đã hiểu về bản chất của Odds là một tỷ số giữa 2 xác suất thành công/thất bại. Odds ratio là một cách để đo lường sự tương phản giữa 2 giá trị Odds tương ứng với 2 bậc giá trị của biến X (2 phác đồ), theo công thức sau:

\[OR = \frac{Odds_{PPOS}}{Odds_{GnRHa}} = \frac{\mu_{PPOS}/(1-\mu_{PPOS})}{\mu_{GnRHa}/(1-\mu_{GnRHa})}\] Vì là 1 tỷ số, OR có thể >1, và dao động từ 0 đến \(+\infty\). Giá trị OR < 1 cho thấy hiệu ứng của phác đồ PPOS thấp hơn so với nhóm tham chiếu, ngược lại OR > 1 và càng lớn càng cho thấy phác đồ PPOS có hiệu quả cao hơn. OR = 1 cho thấy 2 phác đồ có hiệu ứng tương đương nhau.

  1. Marginal risk ratio (RR)

Risk ở đây tương ứng với ý nghĩa xác suất, khả năng, tỷ lệ thành công (giá trị \(\mu\) cho mỗi phác đồ), risk ratio đơn giản là tỷ số giữa 2 risks.

\[RR = \frac{\mu_{PPOS}}{\mu_{GnRHa}} \] RR được diễn giải tương tự như OR, giá trị RR=1 cho thấy 2 phác đồ là tương đương nhau, giá trị RR > 1 cho thấy phác đồ PPOS có hiệu quả cao hơn, và ngược lại RR < 1 cho thấy phác đồ GnRha có hiệu quả cao hơn.

Kết quả OR và RR được trình bày trong bảng sau:

or = avg_comparisons(
  log_mod,
  variables = "Protocol",
  transform_pre = "lnoravg",
  transform_post = "exp")%>%
  dplyr::select(-c(1,2,8:10))%>%
  mutate(contrast = "OR")

rr = avg_comparisons(
  log_mod,
  variables = "Protocol",
  transform_pre = "lnratioavg",
  transform_post = exp)%>%dplyr::select(-c(1,2,8:10))%>%
   mutate(contrast = "RR")

rbind(or,rr) %>% knitr::kable()
contrast estimate p.value conf.low conf.high
OR 2.143148 0.0124863 1.178458 3.897536
RR 1.227801 0.0112964 1.047548 1.439071

Theo kết quả này, cả giá trị OR và RR đều cho thấy một hiệu quả ưu thế hơn của phác đồ PPOS so với GnRHa đối với khả năng đạt được thai diễn tiến. Sự chênh lệch về Odds và xác suất thành công giữa 2 phác đồ là có ý nghĩa thống kê.

Ta cũng có thể tính RD, OR và RR cho các biến định lượng, lúc này 2 bậc so sánh là \(X-1\)\(X+1\), kết quả cho biết hiệu ứng khi X gia tăng 1 đơn vị. Bảng sau đây trình bày giá trị RD, OR và RR cho Age, AFC, Thickness và cho riêng mỗi nhóm phác đồ.

rd = avg_comparisons(
  log_mod,
  variables = list(Age = 1,
                   AFC = 1, 
                   Thickness = 1),
  by = "Protocol")%>%
  dplyr::select(-c(1,7,11:13))%>%
  mutate(contrast = "RD")

rd %>% knitr::kable()
term contrast Protocol estimate std.error p.value conf.low conf.high
AFC RD GnRHa 0.0274809 0.0083964 0.0010643 0.0110244 0.0439375
AFC RD PPOS -0.0132260 0.0089145 0.1378990 -0.0306981 0.0042460
Age RD GnRHa -0.0280614 0.0076484 0.0002435 -0.0430519 -0.0130709
Age RD PPOS -0.0219968 0.0067738 0.0011649 -0.0352732 -0.0087204
Thickness RD GnRHa 0.0211200 0.0141114 0.1344810 -0.0065378 0.0487777
Thickness RD PPOS 0.0165540 0.0113106 0.1433070 -0.0056143 0.0387223
or = avg_comparisons(
  log_mod,
  variables = list(Age = 1,
                   AFC = 1, 
                   Thickness = 1),
  by = "Protocol",
  transform_pre = "lnoravg",
  transform_post = "exp")%>%
  dplyr::select(-c(1))%>%
  mutate(contrast = "OR")

rr = avg_comparisons(
  log_mod,
  variables = list(Age = 1,
                   AFC = 1, 
                   Thickness = 1),
  by = "Protocol",
  transform_pre = "lnratioavg",
  transform_post = exp)%>%
  dplyr::select(-c(1,9:11))%>%
   mutate(contrast = "RR")

rbind(or,rr) %>% knitr::kable()
term contrast Protocol estimate p.value conf.low conf.high
AFC OR GnRHa 1.1275251 0.0009388 1.0501331 1.2106207
AFC OR PPOS 0.9212316 0.1300049 0.8284087 1.0244553
Age OR GnRHa 0.8846513 0.0001927 0.8294515 0.9435247
Age OR PPOS 0.8724480 0.0002967 0.8102853 0.9393797
Thickness OR GnRHa 1.0966212 0.1337642 0.9720682 1.2371335
Thickness OR PPOS 1.1081457 0.1354727 0.9683808 1.2680828
AFC RR GnRHa 1.0435504 0.0019386 1.0157958 1.0720634
AFC RR PPOS 0.9835603 0.1445533 0.9619010 1.0057073
Age RR GnRHa 0.9574040 0.0005782 0.9339622 0.9814341
Age RR PPOS 0.9728020 0.0025959 0.9555020 0.9904153
Thickness RR GnRHa 1.0332998 0.1391176 0.9894065 1.0791403
Thickness RR PPOS 1.0209655 0.1501173 0.9925187 1.0502275

Theo kết quả này, ta thấy AFC có tương quan thuận (OR > 1 và RR > 1) với xác suất thai diễn tiến, nhưng chỉ có ý nghĩa trong nhóm phác đồ GnRHa; Age có tương quan nghịch ý nghĩa với khả năng đạt thai diễn tiến cho cả 2 phác đồ (OR < 1 và RR < 1), trong khi không có liên hệ ý nghĩa nào giữa Thickness và khả năng đạt thai diễn tiến.

Thực ra, những trị số mà ta vừa ước lượng bao gồm RD, OR hoặc RR chỉ có ý nghĩa tóm tắt (abstraction) và cho biết thông tin về hiệu ứng ở mức độ quần thể. Hiện nay, có một số tác giả khuyến cáo về nhược điểm của việc chỉ báo cáo hiệu ứng cho biến nhị phân bằng một trị số duy nhất, và lợi ích của việc cung cấp thông tin chi tiết hơn về toàn thể đặc tính phân phối của hiệu ứng trong mẫu khảo sát.

Biểu đồ sau đây cho phép đối chiếu phân phối toàn thể của xác suất đạt thai diễn tiến cộng dồn trong 107 cá thể dùng phác đồ GnRHa và 99 cá thể dùng phác đồ PPOS.

effs = comparisons(log_mod,
                   variables = "Protocol", 
                   newdata = datagrid(grid_type = 'counterfactual'))

effs %>% ggplot() +
  geom_density(aes(x = predicted, fill = Protocol), alpha = 0.5) +
  geom_vline(xintercept = mean(effs$predicted_lo), color = "blue", 
             linetype = 2, size = 1) +
  geom_vline(xintercept = mean(effs$predicted_hi), color = "red", 
             linetype = 2, size = 1) +
  labs(x = "Probability of OP", 
       title = "Unit level contrast",
       fill = "Protocol")+
  scale_x_continuous(limits = c(0,1), breaks = seq(0,1,0.1))+
  scale_fill_manual(values = pals)+
  theme_bw(10)

Chú thích: Hình trên trình bày 2 biểu đồ mật độ phân phối cho phép so sánh đặc điễm phân phối của xác suất thai diễn tiến (Trục X) giữa 2 phác đồ: GnRHa (màu xanh) và PPOS (màu hồng). 2 đường thẳng không liên tục tương ứng với giá trị trung vị của xác suất này ở mỗi nhóm.

Hình ảnh cho thấy có sự tương phản rõ nét giữa 2 phác đồ, với ưu thế nghiêng về phác đồ PPOS.

Một biểu đồ khác cho phép hình dung về cán cân chênh lệch của hiệu quả giữa 2 phác đồ, đến cấp độ từng cá thể.

effs %>% ggplot(aes(x=predicted_lo, y=predicted_hi))+
  geom_abline(slope = 1, intercept = 0, linetype = 2) +
  geom_point(aes(x=predicted_lo, y=predicted_hi,
                 col = predicted_hi))+
  coord_fixed() +
  scale_x_continuous(limits = c(0,1))+
  scale_y_continuous(limits = c(0,1))+
  scale_color_viridis_c('OP prob')+
  labs(x = "Probability of OP by GnRH_ant", y = "Probability of OP by PPOS")+
  theme_bw()

Chú thích: Đây là một biểu đồ tán xạ cho phép kiểm tra mức độ tương phản / chênh lệch giữa 2 phác đồ về xác suất thai diễn tiến. Trục X và Y trình bày 2 thang đo xác suất từ 0-1 cho mỗi phác đồ. Mỗi chấm tròn biểu thị cho một đơn vị khảo sát (cá thể bệnh nhân), đường phân giác của biểu đồ biểu thị cho giả thuyết là hai phác đồ tương đương hoàn toàn với nhau. Những điểm tròn nằm bên dưới đường phân giác này có hiệu quả GnRH_ant ưu thế hơn, trái lại với những điểm nằm bên trên đường này, phác đồ PPOS có hiệu ứng ưu thế hơn. Những điểm nằm rất gần và dọc theo đường này có hiệu quả tương đương giữa 2 phác đồ.

Các biểu đồ tiếp theo trình bày về mối liên hệ giữa Tuổi, AFC và Thickness với xác suất thai diễn tiến, riêng cho mỗi nhóm phác đồ:

preds = predictions(model = log_mod,
                    newdata = datagrid(Age = seq(20,40,5),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = Age,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
               .width = c(0.75, 0.95),
               point_interval = 'median_qi',
               show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probability", x = "Age") + 
  scale_x_continuous(limits = c(20,45), breaks = seq(20,40,5))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

preds = predictions(model = log_mod,
                    newdata = datagrid(AFC = seq(1,30,5),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = AFC,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
               .width = c(0.75, 0.95),
               point_interval = 'median_qi',
               show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probability", x = "AFC") + 
  scale_x_continuous(limits = c(0,31), breaks = seq(0,30,5))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

preds = predictions(model = log_mod,
                    newdata = datagrid(Thickness = seq(8,20,4),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = Thickness,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
               .width = c(0.75, 0.95),
               point_interval = 'median_qi',
               show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probability", x = "Endometrial thickness") + 
  scale_x_continuous(limits = c(7,22), breaks =seq(8,20,4))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

7 Bước 2B: Mô hình hồi quy Binomial cho tỷ lệ OP/tổng số phôi

Trong phần tiếp theo, ta sẽ phân tích loại kết quả thứ hai: tỷ lệ thai diễn tiến trên tổng số phôi đã chuyển (biến OP_cum).

Mô hình được dựng bằng thư viện gamlss với cú pháp giống như mô hình GLM Binomial trong chương trước. Công thức của mô hình như sau: “cbind(OP_cum, n_ET-OP_cum) ~ Protocol * AFC + Thickness + Age” (Nhắc lại: mô hình GLM Binomial tổng quát cần 2 biến ở vị trí kết quả: số kết quả thành công là biến OP_cum, và số kết quả thất bại, là hiệu số giữa số phôi chuyển và số thai)

tùy chỉnh family = BI(mu.link = “logit”) tương ứng với phân phối Binomial với hàm liên kết logit cho tham số \(\mu\)

Nội dung của mô hình này như sau:

bi_mod = gamlss(cbind(OP_cum, n_ET-OP_cum) ~ 
                  Protocol * AFC + Thickness + Age,
                data = df,
                family = BI(mu.link = "logit"))
## GAMLSS-RS iteration 1: Global Deviance = 462.3237 
## GAMLSS-RS iteration 2: Global Deviance = 462.3237
summary(bi_mod)
## ******************************************************************
## Family:  c("BI", "Binomial") 
## 
## Call:  gamlss(formula = cbind(OP_cum, n_ET - OP_cum) ~ Protocol *  
##     AFC + Thickness + Age, family = BI(mu.link = "logit"),      data = df) 
## 
## Fitting method: RS() 
## 
## ------------------------------------------------------------------
## Mu link function:  logit
## Mu Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)  
## (Intercept)       0.16961    0.90175   0.188   0.8510  
## ProtocolPPOS      1.39994    0.54026   2.591   0.0103 *
## AFC               0.02922    0.02724   1.073   0.2846  
## Thickness         0.04225    0.03605   1.172   0.2426  
## Age              -0.05824    0.02403  -2.423   0.0163 *
## ProtocolPPOS:AFC -0.08438    0.04027  -2.096   0.0374 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## No. of observations in the fit:  206 
## Degrees of Freedom for the fit:  6
##       Residual Deg. of Freedom:  200 
##                       at cycle:  2 
##  
## Global Deviance:     462.3237 
##             AIC:     474.3237 
##             SBC:     494.2909 
## ******************************************************************

Cách thức diễn giải mô hình này hoàn toàn giống như mô hình logistic ở trên nên ta không cần nhắc lại ở đây. Ta sẽ đi thẳng đến công đoạn ước tính 3 trị số marginal effects cho phác đồ PPOS từ mô hình này:

dr = avg_comparisons(
  bi_mod,
  what = "mu",
  variables = "Protocol")%>%
  dplyr::select(-c(1,2,5,6))%>%
  mutate(contrast = "DR")

or = avg_comparisons(
  bi_mod,
  what = "mu",
  variables = "Protocol",
  transform_pre = "lnoravg",
  transform_post = "exp")%>%
  dplyr::select(-c(1,2,8:10))%>%
  mutate(contrast = "OR")

rr = avg_comparisons(
  bi_mod,
  what = "mu",
  variables = "Protocol",
  transform_pre = "lnratioavg",
  transform_post = exp)%>%dplyr::select(-c(1,2,8:10))%>%
   mutate(contrast = "RR")

rbind(dr, or,rr) %>% knitr::kable()
contrast estimate p.value conf.low conf.high
DR 0.0837135 0.0434577 0.0024592 0.1649678
OR 1.4432859 0.0442570 1.0094717 2.0635289
RR 1.2673994 0.0446990 1.0056242 1.5973175

Theo kết quả này, có liên hệ ý nghĩa giữa phác đồ PPOS và sự gia tăng của tỷ lệ thai diễn tiến cộng dồn. Cụ thể nhóm phác đồ PPOS có tỷ lệ thai diễn tiến cao hơn trung bình 8.37% (DR = 0.0837, KTC95%: 0.002 - 0.165, p=0.04). Tỷ số Odds (OR) và tỷ số nguy cơ (RR) lần lượt là 1.44 và 1.27 đều cao hơn 1 cho thấy hiệu quả ưu thế hơn của phác đồ PPOS, cả hai đều có ý nghĩa thống kê.

Ta thực hiện mô tả phân phối toàn thể của hiệu ứng này qua hai biểu đồ sau:

effs = comparisons(bi_mod,
                   what = "mu",
                   variables = "Protocol", 
                   newdata = datagrid(grid_type = 'counterfactual'))

effs %>% ggplot() +
  geom_density(aes(x = predicted, fill = Protocol), alpha = 0.5) +
  geom_vline(xintercept = mean(effs$predicted_lo), color = "blue", 
             linetype = 2, size = 1) +
  geom_vline(xintercept = mean(effs$predicted_hi), color = "red", 
             linetype = 2, size = 1) +
  labs(x = "Probability of OP", 
       title = "Unit level contrast",
       fill = "protocol")+
  scale_x_continuous(limits = c(0,1), breaks = seq(0,1,0.1))+
  scale_fill_manual(values = pals)+
  theme_bw(10)

Hình ảnh này cho thấy mặc dù phần trùng lặp giữa phân phối của xác suất thai diễn tiến ở 2 phác đồ là khá cao, vẫn có một bộ phận cá thể cho thấy hiệu quả ưu thế hơn của phác đồ PPOS, và giá trị trung vị của 2 phân phối cách nhau một khoảng gần bằng 0.1.

Tương tự, trên biểu đồ tương hợp về xác suất thai diễn tiến ở 2 phác đồ, phần lớn cá thể nằm ở vị trí phía trên đường phân giác, cho thấy sự chênh lệch về ưu thế nghiêng về phía phác đồ PPOS.

effs %>% ggplot(aes(x=predicted_lo, y=predicted_hi))+
  geom_abline(slope = 1, intercept = 0, linetype = 2) +
  geom_point(aes(x=predicted_lo, y=predicted_hi,
                 col = predicted_hi))+
  coord_fixed() +
  scale_x_continuous(limits = c(0,1))+
  scale_y_continuous(limits = c(0,1))+
  scale_color_viridis_c('OP prob')+
  labs(x = "Probability of OP by GnRH_ant", y = "Probability of OP by PPOS")+
  theme_bw()

Cuối cùng, ta cũng khảo sát về liên hệ giữa Tuổi, AFC và Thickness với tỷ lệ thai diễn tiến cộng dồn tùy theo phác đồ.

preds = predictions(model = bi_mod,
                    what = "mu",
                    newdata = datagrid(Age = seq(20,40,5),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = Age,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
                    .width = c(0.75, 0.95),
                    point_interval = 'median_qi',
                    show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probability", x = "Age") + 
  scale_x_continuous(limits = c(20,45), breaks = seq(20,40,5))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

preds = predictions(model = bi_mod,
                    what = "mu",
                    newdata = datagrid(AFC = seq(1,30,5),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = AFC,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
               .width = c(0.75, 0.95),
               point_interval = 'median_qi',
               show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probability", x = "AFC") + 
  scale_x_continuous(limits = c(0,31), breaks = seq(0,30,5))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

preds = predictions(model = bi_mod,
                    what = "mu",
                    newdata = datagrid(Thickness = seq(8,20,4),
                                       Protocol = c("GnRHa","PPOS"),
                                       grid_type = "counterfactual"))

preds%>%ggplot(aes(x = Thickness,
                   y = estimate)) +
  stat_halfeye(alpha = 0.5, 
               .width = c(0.75, 0.95),
               point_interval = 'median_qi',
               show.legend = F)+
  stat_lineribbon(aes(y = estimate, fill = Protocol), 
                  .width = c(.95, .75, .50), 
                  alpha = 1/5,
                  show.legend = F) +
  labs(y="OP probabilitys", x = "Endometrial thickness") + 
  scale_x_continuous(limits = c(7,22), breaks =seq(8,20,4))+
  scale_y_continuous(limits = c(0,1))+
  facet_wrap(~Protocol, ncol = 2)+
  theme_bw(10)+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) +
  theme(axis.text = element_text(size = 10, color = "black"),
        axis.title = element_text(size = 10, color = "black"))

8 Diễn giải kết quả của phân tích

Phân tích dữ liệu này cho phép rút ra một số kết luận như sau:

  • Khả năng đạt được thai diễn tiến có thể được khảo sát dưới nhiều hình thức: xác suất quan sát được một kết quả thành công (biến nhị giá 0,1) trên cá thể bệnh nhân, xác suất thành công cho mỗi đơn vị noãn - hay tỷ lệ thành công trên tổng số phôi được chuyền. Cách thức khảo sát có thể ảnh hưởng đến kết quả phân tích thống kê (trong trường hợp hiện thời, cả 2 cách khảo sát đều cho ra thông điệp tương đồng, tuy nhiên có sự khác biệt về giá trị OR, RR hoặc RD).

  • Dù khảo sát dưới hình thức nào, thì đều dẫn về công cụ chung là mô hình GLM với phân phối Binomial với 2 tham số \(n\),\(\mu\). Mô hình logistic cổ điển là trường hợp đặc biệt khi \(n=1\). Cách diễn giải và suy luận thống kê là như nhau cho cả 2 mô hình.

  • Mô hình logistic (hay GLM Binomial) cho phép suy luận thống kê về mối liên hệ giữa 1 biến độc lập và một kết quả xác suất. Ta có thể áp dụng mô hình này cho những kết cục lâm sàng nhị phân, hoặc tỷ lệ.

LS0tDQp0aXRsZTogIkto4bqjbyBzw6F0IGvhur90IGPhu6VjIG5o4buLIHBow6JuOiBI4buTaSBxdXkgbG9naXN0aWMiDQphdXRob3I6ICJCUy4gTmfhu41jIENow6J1LCBCUy4gS2jhuqMgTmhpIg0KZGF0ZTogIjA2IFRow6FuZyAzIG7Eg20gMjAyMyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBkZWZhdWx0DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGRldjogc3ZnDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIGxhdGV4X2VuZ2luZTogbHVhbGF0ZXgNCiAgICBrZWVwX3RleDogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGKQ0KYGBgDQoNCiMgR2nhu5tpIHRoaeG7h3UNCg0K4bueIGNoxrDGoW5nIG7DoHksIGNow7puZyB0YSBz4bq9IGto4bqjbyBzw6F0IHRow6BuaCBxdeG6oyDEkcaw4bujYyBxdWFuIHTDom0gbmjhuqV0IHRyb25nIG5naGnDqm4gY+G7qXUgduG7gSBo4buXIHRy4bujIHNpbmggc+G6o24sIMSRw7MgbMOgIHPhu7EgdGjhu6UgdGhhaSAodGhhaSBzaW5oIGjDs2EsIGzDom0gc8OgbmcpLCBz4buxIHRp4bq/cCBkaeG7hW4gdGjDoG5oIGPDtG5nIGPhu6dhIHRoYWkga8OsICh0aGFpIGRp4buFbiB0aeG6v24pIHbDoCBz4buxIHNpbmggbuG7ny4gVHJvbmcgeSB2xINuLCBuaOG7r25nIGvhur90IGPhu6VjIG7DoHkgdGjGsOG7nW5nIMSRxrDhu6NjIGto4bqjbyBzw6F0IG5oxrAgbeG7mXQgZ2nDoSB0cuG7iyBtYW5nIHTDrW5oIG5o4buLIHBow6JuIChDw7MgaG/hurdjIGtow7RuZyksIGhv4bq3YyB04bu3IGzhu4cgdGjDoG5oIGPDtG5nIHRyw6puIHThu5VuZyBz4buRIGPDoSB0aOG7gyBi4buHbmggbmjDom4uIFR1eSBuaGnDqm4sIGLhuqNuIGNo4bqldCBj4bunYSBsb+G6oWkga+G6v3QgY+G7pWMgbsOgeSBjw7MgdGjhu4MgxJHGsOG7o2MgbmjDrG4gbmjhuq1uIG5oxrAgbeG7mXQgeMOhYyBzdeG6pXQgaGF5IGto4bqjIG7Eg25nIHRow6BuaCBjw7RuZyB2w6AgY8WpbmcgY8OzIHTDrW5oIGvhur8gdGjhu6thIG5oxrAgbeG7mXQgcGjhuqduIHRyb25nIGNodeG7l2kga+G6v3QgcXXhuqMgY+G7p2EgdGnhur9uIHRyw6xuaCB0aOG7pSB0aW5oIG5ow6JuIHThuqFvLiBWw6wgduG6rXksIHRhIGPDsyB0aOG7gyDDoXAgZOG7pW5nIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIG5o4buLIHRo4bupYyAoYmlub21pYWwpIMSR4buDIGto4bqjbyBzw6F0IGNow7puZy4NCg0KVGjDrSBk4bulLCBr4bq/dCBxdeG6oyB0aGFpIGRp4buFbiB0aeG6v24gdGjGsOG7nW5nIMSRxrDhu6NjIGdoaSBuaOG6rW4gYuG6sW5nIDIgZ2nDoSB0cuG7iyAwIChraMO0bmcpIGhv4bq3YyAxIChjw7MpIHRyb25nIGThu68gbGnhu4d1LCBuaMawbmcgYuG6o24gY2jhuqV0IGPhu6dhIHRoYWkgZGnhu4VuIHRp4bq/biBjw7MgdGjhu4MgeGVtIG5oxrAgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nICRwJCBj4bunYSAxIHRo4butIG5naGnhu4dtIGNodXnhu4NuIHBow7RpLiBDaMOtbmggdsOsIHRyw6puIHRo4buxYyB04bq/LCBz4buRIGzGsOG7o25nIHBow7RpIMSRxrDhu6NjIGNodXnhu4NuIHLhuqV0IHRo4bqlcCwgY2jhu4kgZ2nhu5tpIGjhuqFuIOG7nyAxIMSR4bq/biAyIHBow7RpIGNobyBt4buXaSBjaHUga8OsLCBuw6puIGvhur90IHF14bqjIHF1YW4gc8OhdCDEkcaw4bujYyBt4bubaSBiaeG7g3UgaGnhu4duIHRow6BuaCBnacOhIHRy4buLIDAgaG/hurdjIDEuDQoNClRyb25nIGNoxrDGoW5nIG7DoHksIHRhIHPhur0gdMOsbSBoaeG7g3UgduG7gSBwaMOibiB0w61jaCBo4buTaSBxdXkgbG9naXN0aWMgdsOgIHRyxrDhu51uZyBo4bujcCB04buVbmcgcXXDoXQgY+G7p2EgbsOzIC0gbeG7mXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBHTE0gduG7m2kgcGjDom4gcGjhu5FpIEJpbm9taWFsLCBjaG8gcGjDqXAgc3V5IGRp4buFbiB0aOG7kW5nIGvDqiBjaG8ga+G6v3QgcXXhuqMgbmjhu4sgcGjDom4gaG/hurdjIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZyBj4bunYSB0aOG7rSBuZ2hp4buHbS4NCg0KVMOsbmggaHXhu5FuZyBtaW5oIGjhu41hIGzDoCBt4buZdCB0aOG7rSBuZ2hp4buHbSBsw6JtIHPDoG5nIHbhu5tpIG3hu6VjIHRpw6p1IHNvIHPDoW5oIGhp4buHdSBxdeG6oyBj4bunYSBwaMOhYyDEkeG7kyBQUE9TIHNvIHbhu5tpIHBow6FjIMSR4buTIHRoYW0gY2hp4bq/dSBsw6AgR25SSF9hbnQgxJHhu5FpIHbhu5tpIGto4bqjIG7Eg25nIMSR4bqhdCDEkcaw4bujYyB0aGFpIGRp4buFbiB0aeG6v24gKG9uZ29pbmcgcHJlZ25hbmN5KS4gVHJvbmcgdGjDrSBuZ2hp4buHbSBuw6B5LCBwaMOhYyDEkeG7kyBHblJIX2FudCDEkcaw4bujYyDDoXAgZOG7pW5nIGNobyBuaMOzbSDEkeG7kWkgY2jhu6luZyBn4buTbSAxMDcgcGjhu6UgbuG7ryBoaeG6v20gbXXhu5luLCB2w6AgcGjDoWMgxJHhu5MgUFBPUyDDoXAgZOG7pW5nIGNobyBuaMOzbSBjYW4gdGhp4buHcCBn4buTbSA5OSB0csaw4budbmcgaOG7o3AuIFNhdSBraGkgdGh1IGhv4bqhY2ggbm/Do24sIHRo4bulIHRpbmggdsOgIHRy4buvIHBow7RpLCBuZ8aw4budaSB0YSB0aOG7sWMgaGnhu4duIHThu6sgMSDEkeG6v24gMyBs4bqnbiBjaHV54buDbiBwaMO0aSwgbeG7l2kgbOG6p24gMS0yIHBow7RpIHbDoCBnaGkgbmjhuq1uIGvhur90IHF14bqjIHRoYWkgZGnhu4VuIHRp4bq/biBj4buZbmcgZOG7k24gKG7hur91IG3hu5l0IHRyxrDhu51uZyBo4bujcCBraMO0bmcgdGjDoG5oIGPDtG5nIOG7nyBs4bqnbiBjaHV54buDbiBwaMO0aSDEkeG6p3UgdGnDqm4sIHRhIHPhur0gbOG6t3AgbOG6oWkgcXV5IHRyw6xuaCBjaHV54buDbiBwaMO0aSBs4bqnbiAyLCBob+G6t2MgbOG6p24gMykuIFRo4butIG5naGnhu4dtIGvhur90IHRow7pjIOG7nyBs4bqnbiB0aOG7qSAzLiANCg0KS+G6v3QgY+G7pWMgc+G6vSDEkcaw4bujYyBraOG6o28gc8OhdCBkxrDhu5tpIDMgaMOsbmggdGjhu6ljOg0KDQoxKSBHacOhIHRy4buLIG5o4buLIHBow6JuOiB0aMOgbmggY8O0bmcgaGF5IHRo4bqldCBi4bqhaSB0dXnhu4d0IMSR4buRaSA6IGPDsyDEkeG6oXQgxJHGsOG7o2MgdGhhaSBkaeG7hW4gdGnhur9uIGhheSBraMO0bmcgKGNobyB04buRaSDEkWEgMyBs4bqnbik6IDAgPSBLaMO0bmcsIDEgPSBDw7MgKGLhuqV0IGvhu4Mgc29uZyB0aGFpLCDEkcahbiB0aGFpKQ0KDQoyKSBU4bqnbiBzdeG6pXQ6IHPhu5EgbMaw4bujbmcgdGhhaSBkaeG7hW4gdGnhur9uIGNobyBt4buXaSBjw6EgdGjhu4MgKGdpw6EgdHLhu4sgPSAwIChraMO0bmcgxJHhuqF0IMSRxrDhu6NjKSwgMSA9IMSRxqFuIHRoYWkgZGnhu4VuIHRp4bq/biwgMiA9IHNvbmcgdGhhaSBkaeG7hW4gdGnhur9uKS4NCg0KMykgVOG7tyBs4buHIMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gPSBz4buRIHRoYWkgZGnhu4VuIHRp4bq/biBjaGlhIGNobyB04buVbmcgc+G7kSBwaMO0aSDEkcaw4bujYyBjaHV54buDbi4gVGjDrSBk4bulOiBM4bqnbiAxIGNodXnhu4NuIDIgcGjDtGksIGvhur90IHF14bqjIGtow7RuZyDEkeG6oXQsIGzhuqduIDIgY2h1eeG7g24gMiBwaMO0aSB2w6AgxJHhuqF0IMSRxqFuIHRoYWksIHRow6wgdOG7tyBs4buHIHRow6BuaCBjw7RuZyA9ICQxLygyKzIpID0gMC4yNSQNCg0KTMawdSDDvSBy4bqxbmcgY8OhYyB0csaw4budbmcgaOG7o3A6IHPhuqt5IHRoYWksIHRoYWkgbmdvw6BpIHThu60gY3VuZy4uLiB4ZW0gbmjGsCB0aOG6pXQgYuG6oWkuDQoNCiMgS+G6vyBob+G6oWNoIHBow6JuIHTDrWNoDQoNCljDoWMgc3XhuqV0IMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gY+G7mW5nIGThu5NuIGtoaSDDoXAgZOG7pW5nIHBow6FjIMSR4buTIFBQT1MsIHNvIHbhu5tpIHBoxrDGoW5nIHBow6FwIHRoYW0gY2hp4bq/dSBsw6AgR25SSF9hbnQgc+G6vSDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgYuG6sW5nIG1hcmdpbmFsIE9kZHMtcmF0aW8gKE9SKSB2w6Agcmlzay1yYXRpbyAoUlIpLCBjw7MgaGnhu4d1IGNo4buJbmggY2hvIFR14buVaSwgQUZDIHbDoCDEkeG7mSBkw6B5IG7hu5lpIG3huqFjIHRydW5nIGLDrG5oIG5nw6B5IGNodXnhu4NuIHBow7RpLCBk4buxYSB2w6BvIG3hu5l0IG3DtCBow6xuaCBo4buTaSBxdXkgbG9naXN0aWMuIFN1eSBkaeG7hW4gdGjhu5FuZyBrw6ogZOG7sWEgdsOgbyBwaOG7pyBuaOG6rW4gZ2nhuqMgdGh1eeG6v3QgdsO0IGhp4buHdSAoT1IgdsOgIFJSID0gMSkg4bufIG5nxrDhu6FuZyDDvSBuZ2jEqWEgcCA9IDAuMDUuDQoNCiMgQ8O0bmcgY+G7pSBj4bqnbiB0aGnhur90IGNobyBxdXkgdHLDrG5oDQoNCisgVGjGsCB2aeG7h24gZHBseXIgdHJvbmcgaOG7hyBzaW5oIHRow6FpIHRpZHl2ZXJzZSDEkeG7gyB0aGFvIHTDoWMgZOG7ryBsaeG7h3UgdsOgIHRo4buRbmcga8OqIG3DtCB04bqjDQoNCisgVGjGsCB2aeG7h24gZ2dwbG90MiwgZ2dyaWRlcywgdGlkeWJheWVzIHbDoCBnZ2Rpc3QgxJHhu4MgduG6vSBt4buZdCBz4buRIGJp4buDdSDEkeG7kyB0aOG7kW5nIGvDqjsNCg0KKyBUaMawIHZp4buHbiBwYXRjaHdvcmsgxJHhu4MgZ2jDqXAgbmhp4buBdSBiaeG7g3UgxJHhu5MgZ2dwbG90MiB24bubaSBuaGF1Lg0KDQorIFRoxrAgdmnhu4duIGdhbWxzcyDEkeG7gyBk4buxbmcgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgQmlub21pYWwNCg0KKyBUaMawIHZp4buHbiBtYXJnaW5hbGVmZmVjdHMgxJHhu4MgxrDhu5tjIHTDrW5oIE9SLCBSUiB2w6Agc3V5IGRp4buFbiB0aOG7kW5nIGvDqiB04burIGvhur90IHF14bqjIG3DtCBow6xuaC4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dyaWRnZXMpDQoNCmxpYnJhcnkoZ2FtbHNzKQ0KbGlicmFyeShtYXJnaW5hbGVmZmVjdHMpDQoNCmxpYnJhcnkodGlkeWJheWVzKQ0KbGlicmFyeShnZ2Rpc3QpDQpsaWJyYXJ5KHBhdGNod29yaykNCmBgYA0KDQrEkOG6p3UgdGnDqm4sIHRhIHThuqNpIGThu68gbGnhu4d1IHThu6sgZmlsZSAnUFBPU19PUC5jc3YnIHbDoG8gZGF0YWZyYW1lIGRmLCBzYXUgxJHDsyB0YSANCg0KYGBge3J9DQpkZiA9IHJlYWQuY3N2KCdQUE9TX09QLmNzdicsIHNlcCA9ICc7JywgDQogICAgICAgICAgICAgIGRlYyA9ICcsJywgDQogICAgICAgICAgICAgIGZpbGVFbmNvZGluZyA9ICdVVEYtOC1CT00nKSU+JQ0KICBuYS5vbWl0KCkNCg0KZGYkUHJvdG9jb2wgPSBmYWN0b3IoZGYkUHJvdG9jb2wpDQoNCmRmJT4lZHBseXI6OnNhbXBsZV9mcmFjKDAuMyklPiVoZWFkKCklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNClRyb25nIGThu68gbGnhu4d1IG7DoHk6DQoNCkFnZSA9IFR14buVaSBi4buHbmggbmjDom4sDQoNCkFGQyA9IEThu7EgdHLhu68gYnXhu5NuZyB0cuG7qW5nIGPGoSBi4bqjbjsNCg0KVGhpY2tuZXNzID0gxJHhu5kgZMOgeSBu4buZaSBt4bqhYyB04butIGN1bmc7IA0KDQpuX0VUOiB04buVbmcgc+G7kSBwaMO0aSDEkcOjIGNodXnhu4NuIHRyb25nIHRp4bq/biB0csOsbmggbmdoacOqbiBj4bupdTsNCg0KT1BfY3VtID0gdOG6p24gc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biBj4buZbmcgZOG7k24gKGdpw6EgdHLhu4sgY2FvIG5o4bqldCA9IDIsIHRo4bqlcCBuaOG6pXQgPSAwKTsNCg0KT1BfYmluID0ga+G6v3QgcXXhuqMgdGjDoG5oIGPDtG5nL3Ro4bqldCBi4bqhaSB0dXnhu4d0IMSR4buRaSAoMiBnacOhIHRy4buLIDAsMSkNCg0KT1BfcmF0ZSA9IHThu7cgbOG7hyB0aGFpIGRp4buFbiB0aeG6v24gPSBPUF9jdW0gLyBuX0VUDQoNCiMgQsaw4bubYyAxOiBQaMOibiB0w61jaCBtw7QgdOG6ow0KDQpUYSBr4bq/dCBo4bujcCBow6BtIGdyb3VwX2J5IHbDoCBzdW1tYXJpemUgY+G7p2EgdGjGsCB2aeG7h24gZHBseXIgxJHhu4MgdGjhu7FjIGhp4buHbiB0aOG7kW5nIGvDqiBtw7QgdOG6oyBjaG8gbmjhu69uZyBow6xuaCB0aOG7qWMga2jDoWMgbmhhdSBj4bunYSBr4bq/dCBj4bulYyB0aGFpIGRp4buFbiB0aeG6v24gY+G7mW5nIGThu5NuOg0KDQphKSBLaOG6o28gc8OhdCB04bqnbiBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uIChiaeG6v24gT1BfY3VtKQ0KDQrEkMOieSBsw6AgbeG7mXQgYmnhur9uIHPhu5EgcuG7nWkgcuG6oWMgdsOgIGNo4buJIGPDsyB0aOG7gyBjw7MgMyBnacOhIHRy4buLOiAwLCAxIGhv4bq3YyAyLiBL4bq/dCBxdeG6oyBtw7QgdOG6oyBuaMawIHNhdToNCg0KYGBge3J9DQpnZXRtb2RlIDwtIGZ1bmN0aW9uKHYpIHsNCiAgIHVuaXF2IDwtIHVuaXF1ZSh2KQ0KICAgdW5pcXZbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHYsIHVuaXF2KSkpXQ0KfQ0KDQpkZiU+JWdyb3VwX2J5KFByb3RvY29sKSU+JQ0KICBzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIFN1bSA9IHN1bShPUF9jdW0pLA0KICAgICAgICAgICAgbWVhbiA9IHNwcmludGYoIiUwLjJmIiwgbWVhbihPUF9jdW0pKSwNCiAgICAgICAgICAgIE1lZGlhbiA9IHNwcmludGYoIiUwLjJmIiwgbWVkaWFuKE9QX2N1bSkpLA0KICAgICAgICAgICAgTW9kZSA9IHNwcmludGYoIiUwLjJmIiwgZ2V0bW9kZShPUF9jdW0pKSwNCiAgICAgICAgICAgIFNEID0gc3ByaW50ZigiJTAuMmYiLCBzZChPUF9jdW0pKSwNCiAgICAgICAgICAgIHA1ID0gc3ByaW50ZigiJTAuMmYiLCBxdWFudGlsZShPUF9jdW0sIDAuMDUpKSwNCiAgICAgICAgICAgIHA5NSA9IHNwcmludGYoIiUwLjJmIiwgcXVhbnRpbGUoT1BfY3VtLCAwLjk1KSksDQogICAgICAgICAgICApJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpUaGVvIGvhur90IHF14bqjIG7DoHksIG7hur91IHjDqXQgduG7gSBz4buRIGzGsOG7o25nIHR1eeG7h3QgxJHhu5FpLCB0aMOsIGTGsOG7nW5nIG5oxrAgZ2nhu69hIDIgcGjDoWMgxJHhu5MgUFBPUyB2w6AgR25SSCBraMO0bmcgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IG7DoG8gY+G6ozogTW9kZSA9IDEgY2hvIHRo4bqleSBr4bq/dCBxdeG6oyB0aMaw4budbmcgZ+G6t3AgbmjhuqV0IGNobyBj4bqjIDIgcGjDoWMgxJHhu5MgbMOgIMSR4bqhdCDDrXQgbmjhuqV0IDEgdGhhaSBkaeG7hW4gdGnhur9uLiBYw6l0IGLDrG5oIHF1w6JuIHRow6wgbmjDs20gUFBPUyBjw7MgduG6uyDGsHUgdGjhur8gaMahbjogc+G7kSB0aGFpIGRp4buFbiB0aeG6v24gdHJ1bmcgYsOsbmggbMOgIDEuMDMgc28gduG7m2kgMC44MTsgY+G6oyAyIMSR4buBdSBjw7MgxJHhu5kgcGjDom4gdMOhbiBjYW8gdMawxqFuZyDEkcawxqFuZyAoU0Qga2hv4bqjbmcgMC43KS4NCg0KYikgTuG6v3UgeMOpdCB24buBIHThu7cgbOG7hyB0aGFpIGRp4buFbiB0aeG6v24gdHLDqm4gdOG7lW5nIHPhu5EgcGjDtGkgY2h1eeG7g246DQoNCmBgYHtyfQ0KZGYlPiVncm91cF9ieShQcm90b2NvbCklPiUNCiAgc3VtbWFyaXplKG4gPSBuKCksDQogICAgICAgICAgICBNZWFuID0gc3ByaW50ZigiJTAuM2YiLCBtZWFuKE9QX3JhdGUpKSwNCiAgICAgICAgICAgIE1lZGlhbiA9IHNwcmludGYoIiUwLjNmIiwgbWVkaWFuKE9QX3JhdGUpKSwNCiAgICAgICAgICAgIFNEID0gc3ByaW50ZigiJTAuM2YiLCBzZChPUF9yYXRlKSksDQogICAgICAgICAgICBwNSA9IHNwcmludGYoIiUwLjNmIiwgcXVhbnRpbGUoT1BfcmF0ZSwgMC4wNSkpLA0KICAgICAgICAgICAgcDk1ID0gc3ByaW50ZigiJTAuM2YiLCBxdWFudGlsZShPUF9yYXRlLCAwLjk1KSksDQogICAgICAgICAgICApJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpL4bq/dCBxdeG6oyBuw6B5IGNobyB0aOG6pXkgdOG7syBs4buHIHRoYWkgZGnhu4VuIHRp4bq/biB0cnVuZyBiw6xuaCBjw7MgduG6uyDGsHUgdGjhur8gaMahbiDhu58gbmjDs20gUFBPUyBzbyB24bubaSBHblJIYW50OiAwLjQ2IHNvIHbhu5tpIDAuMzcgKG7Ds2kgY8OhY2gga2jDoWMsIHjDoWMgc3XhuqV0IHRo4bulIHRoYWkgdGjDoG5oIGPDtG5nIGNobyBt4buXaSBwaMO0aSBjaHV54buDbiBjw7MgduG6uyBjYW8gaMahbiDhu58gcGjDoWMgxJHhu5MgUFBPUyk7IFRhIGPDsyB0aOG7gyDGsOG7m2MgdMOtbmggUmlzayByYXRpbyAoUlIpIGdp4buvYSAyIHBow6FjIMSR4buTOiAkUlIgPSAwLjQ1Ny8wLjM3ID0gMS4yMzUkLiBUdXkgbmhpw6puIGdpw6EgdHLhu4sgdHJ1bmcgduG7iyBs4bqhaSB0xrDGoW5nIMSRxrDGoW5nICgwLjUpLiBOw7NpIGPDoWNoIGtow6FjLCBu4bq/dSBj4bupIGNodXnhu4NuIHBow7RpIDIgbOG6p24gdGjDrCBk4buxIGtp4bq/biDDrXQgbmjhuqV0IHPhur0gY8OzIDEgbOG6p24gxJHhuqF0IHRow6BuaCBjw7RuZy4NCg0KYykgVOG7tyBz4buRIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZy90aOG6pXQgYuG6oWkgKE9kZHMpDQoNClRhIGPDsyB0aOG7gyDGsOG7m2MgbMaw4bujbmcga+G6v3QgcXXhuqMgdGhhaSBkaeG7hW4gdGnhur9uIHRoZW8gbeG7mXQgY8OhY2gga2jDoWMsIGLhurFuZyB04bu3IHPhu5EgT2Rkcw0KDQokJE9kZHMgPSBcZnJhY3twXzF9e3BfMH0gPSBcZnJhY3twXzF9eygxIC0gcF8xKX0kJA0KDQpW4bubaSAkcF8xJCBsw6AgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nLCAkcF8wJCBsw6AgeMOhYyBzdeG6pXQgdGjhuqV0IGLhuqFpLg0KDQpgYGB7cn0NCmRmJT4lbXV0YXRlKE9kZHMgPSBPUF9yYXRlLygxLU9QX3JhdGUpKS0+b2RkX2RmDQoNCm9kZF9kZiRPZGRzWyFpcy5maW5pdGUob2RkX2RmJE9kZHMpXSA8LSBOQQ0KDQpvZGRfZGYlPiUNCiAgZ3JvdXBfYnkoUHJvdG9jb2wpJT4lDQogIHN1bW1hcml6ZShuID0gbigpLA0KICAgICAgICAgICAgTWVhbiA9IHNwcmludGYoIiUwLjNmIiwgbWVhbihPZGRzLCBuYS5ybT1UUlVFKSksDQogICAgICAgICAgICBNZWRpYW4gPSBzcHJpbnRmKCIlMC4zZiIsIG1lZGlhbihPZGRzLG5hLnJtPVRSVUUpKSwNCiAgICAgICAgICAgIFNEID0gc3ByaW50ZigiJTAuM2YiLCBzZChPZGRzLCBuYS5ybT1UUlVFKSksDQogICAgICAgICAgICBwNSA9IHNwcmludGYoIiUwLjNmIiwgcXVhbnRpbGUoT2RkcywgMC4wNSwgbmEucm09VFJVRSkpLA0KICAgICAgICAgICAgcDk1ID0gc3ByaW50ZigiJTAuM2YiLCBxdWFudGlsZShPZGRzLCAwLjk1LG5hLnJtPVRSVUUpKSwNCiAgICAgICAgICAgICklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNClRoZW8ga+G6v3QgcXXhuqMgbsOgeSwgT2RkcyBj4bunYSBuaMOzbSBQUE9TIGNhbyBoxqFuIE9kZHMgY+G7p2EgbmjDs20gR25SSGFudCAoMC41NTYgc28gduG7m2kgMC40NjIpLCBuw7NpIGPDoWNoIGtow6FjIE9kZHMgcmF0aW8gZ2nhu69hIFBQT1MgdsOgIEduUkhhIDogJE9SID0gMC41NTYvMC40NjIgPSAxLjIwMyQNCg0KZCkgTuG6v3UgeMOpdCBnacOhIHRy4buLIHRow6BuaCBjw7RuZyBoYXkgdGjhuqV0IGLhuqFpIHR1eeG7h3QgxJHhu5FpIChiaeG6v24gbmjhu4sgZ2nDoSBPUF9iaW4pDQoNCktoaSB0aOG7sWMgaGnhu4duIHBow6JuIHTDrWNoIG7DoHksIMSRxqFuIHbhu4sgcXVhbiBzw6F0IGzDoCBjw6EgdGjhu4MgYuG7h25oIG5ow6JuIHRoYXkgdsOsIMSRxqFuIHbhu4sgcGjDtGkuIEPDoWNoIGto4bqjbyBzw6F0IG7DoHkgc+G6vSBjaG8gcmEgbeG7mXQga+G6v3QgcXXhuqMga2jDoWMNCg0KYGBge3J9DQpkZiU+JWdyb3VwX2J5KFByb3RvY29sKSU+JQ0KICBzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIFJhdGUgPSBtZWFuKE9QX2JpbiksDQogICAgICAgICAgICBGcmVxID0gc3VtKE9QX2JpbiksDQogICAgICAgICAgICBPZGRzID0gUmF0ZS8oMS1SYXRlKSwNCiAgICAgICAgICAgICklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNCkThu7FhIHbDoG8ga+G6v3QgcXXhuqMgbsOgeSwgdOG7tyBz4buRIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZyBnaeG7r2EgMiBwaMOhYyDEkeG7kyA6ICRSUiA9IDAuNzk4LzAuNjQ1ID0gMS4yMzckIHbDoCBPZGRzLXJhdGlvIGdp4buvYSBjaMO6bmcgJE9SID0gMy45NS8xLjgxNSA9IDIuMTc2JC4g4bueIMSRw6J5IHRhIHRo4bqleSBt4buZdCDEkWnhu4NtIHRow7ogduG7iyBsw6Aga2hpIGNo4buNbiDEkcahbiB24buLIGto4bqjbyBzw6F0IGzDoCBi4buHbmggbmjDom4sIGvhur90IHF14bqjIE9SIHbDoCBSUiBjw7Mga2h1eW5oIGjGsOG7m25nIGNhbyBoxqFuIHNvIHbhu5tpIGtoaSB4w6l0IMSRxqFuIHbhu4sga2jhuqNvIHPDoXQgbMOgIHBow7RpLg0KDQpUYSBs4bqnbiBsxrDhu6N0IGto4bqjbyBzw6F0IHRy4buxYyBxdWFuIDMgaMOsbmggdGjhu6ljIGto4bqjbyBzw6F0IGvhur90IHF14bqjIHRoYWkgZGnhu4VuIHRp4bq/biB0cm9uZyAzIGJp4buDdSDEkeG7kyBzYXUNCg0KYGBge3J9DQpwYWxzID0gYygiIzBmYWFmMiIsIiNmMjBmNGYiKQ0KDQpwMSA9IGRmICU+JSBnZ3Bsb3QoYWVzKHkgPSBPUF9jdW0sIA0KICAgICAgICAgICAgICAgICAgeCA9IFByb3RvY29sLCANCiAgICAgICAgICAgICAgICAgIGZpbGw9IFByb3RvY29sKSkgKyANCiAgZ2VvbV9qaXR0ZXIoYWVzKGZpbGwgPSBQcm90b2NvbCksDQogICAgICAgICAgICAgIHNoYXBlID0gMjEsDQogICAgICAgICAgICAgIGFscGhhID0gMC44LA0KICAgICAgICAgICAgICB3aWR0aCA9IDAuMSwNCiAgICAgICAgICAgICAgaGVpZ2h0ID0gMC4yLA0KICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IFQpKw0KICBsYWJzKHk9IkN1bW11bGF0ZWQgT1AiLCB4ID0gIlByb3RvY29sIikgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFscywgbmFtZSA9ICJQcm90b2NvbCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoMCwxLDIsMykpKw0KICB0aGVtZV9idygxMCkgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSkNCg0KcDIgPSBkZiAlPiUgZ2dwbG90KGFlcyh4ID0gT1BfcmF0ZSwgDQogICAgICAgICAgICAgICAgICB5ID0gUHJvdG9jb2wsIA0KICAgICAgICAgICAgICAgICAgZmlsbD0gUHJvdG9jb2wpKSArIA0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyhmaWxsID0gUHJvdG9jb2wpLA0KICAgICAgICAgICAgICAgICAgICAgIHN0YXQgPSAiYmlubGluZSIsDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGUgPSAwLjUsIA0KICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAzMCwNCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuOCkrDQogIGxhYnMoeD0iQ3VtbXVsYXRlZCBPUCByYXRlIiwgeSA9ICJQcm90b2NvbCIpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHMsIG5hbWUgPSAiUHJvdG9jb2wiKSArDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoMTApDQoNCnAzID0gZGYlPiUNCiAgZ3JvdXBfYnkoUHJvdG9jb2wpJT4lDQogIHN1bW1hcmlzZShTdWNjZXNzID0gbWVhbihPUF9iaW4pLA0KICAgICAgICAgICAgRmFpbHVyZSA9IDEgLSBTdWNjZXNzKSU+JQ0KICBnYXRoZXIoYygyLDMpLGtleSA9ICJPUCIsIHZhbHVlID0gIlJhdGUiKSU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBQcm90b2NvbCwgeSA9IFJhdGUpKSsNCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBPUCksIHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2MwYmVjMiIsIiNmMDNhNzkiKSwgbmFtZSA9ICJPUCBPdXRjb21lIikgKw0KICB0aGVtZV9idygxMCkNCg0KDQpwMyArIHAxL3AyDQpgYGANCg0KQ2jDuiB0aMOtY2g6IEjDrG5oIGLDqm4gdHLDoWkgbMOgIGJp4buDdSDEkeG7kyBj4buZdCBjaOG7k25nIHRyw6xuaCBiw6B5IHBow6JuIGLhu5EgdOG7tyBs4buHIFRow6BuaCBjw7RuZy9UaOG6pXQgYuG6oWkgKHRy4bulYyBZKSBnaeG7r2EgMiBwaMOhYyDEkeG7kyAodHLhu6VjIFgpLCBow6xuaCBiw6puIHBo4bqjaSBn4buTbSAyIGJp4buDdSDEkeG7kzogZ8OzYyB0csOqbiBsw6AgYmnhu4N1IMSR4buTIHTDoW4geOG6oSBzbyBzw6FuaCB04bqnbiBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uICh0cuG7pWMgWSkgZ2nhu69hIDIgcGjDoWMgxJHhu5MgKHRy4bulYyBYKSwgZ8OzYyBkxrDhu5tpIGzDoCBiaeG7g3UgxJHhu5MgdOG6p24gc3XhuqV0IG3DtCB04bqjIHThu7cgbOG7hyB0aGFpIGRp4buFbiB0aeG6v24gY+G7mW5nIGThu5NuICh0cuG7pWMgWSkgZ2nhu69hIDIgcGjDoWMgxJHhu5MgKHRy4bulYyBYKS4NCg0KTmdvw6BpIDIgcGjDoWMgxJHhu5MsIHRyb25nIGThu68gbGnhu4d1IGPDsm4gY8OzIG3hu5l0IHPhu5EgaGnhu4dwIGJp4bq/biBraMOhYyBuaMawIFR14buVaSwgQUZDIHbDoCDEkeG7mSBkw6B5IG7hu5lpIG3huqFjIGPDsyBi4bqjbiBjaOG6pXQgbMOgIGJp4bq/biBz4buRIGxpw6puIHThu6VjLiBUYSBtdeG7kW4ga2jhuqNvIHPDoXQgdHLhu7FjIHF1YW4gbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2Egbmjhu69uZyBoaeG7h3AgYmnhur9uIG7DoHkgdsOgIHjDoWMgc3XhuqV0IMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v247IHRhIGPDsyB0aOG7gyB0aOG7sWMgaGnhu4duIHRoZW8gMyBjw6FjaDoNCg0KKDEpIExpw6puIGjhu4cgYmnhur9uIFggdsOgIGdpw6EgdHLhu4sgMCwxIGPhu6dhIGJp4bq/biBPUF9iaW4sIHRow7RuZyBxdWEgxJHhu5MgdGjhu4sgY+G7p2EgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgYmlub21pYWwgKMSR4buTIHRo4buLIGjDoG0gbG9naXN0aWMpDQoNCigyKSBMacOqbiBo4buHIGJp4bq/biBYIHbDoCBnacOhIHRy4buLIHThu7cgbOG7hyBPUF9yYXRlLCB0aMO0bmcgcXVhIDEgbcO0IGjDrG5oIGjhu5NpIHF1eSBHTE0gduG7m2kgcGjDom4gcGjhu5FpIHF1YXNpYmlub21pYWw7DQoNCigzKSBMacOqbiBo4buHIGJp4bq/biBYIHbDoCBPZGRzLCB0aMO0bmcgcXVhIG3hu5l0IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaA0KDQpgYGB7cn0NCnA0ID0gZGYlPiVnYXRoZXIoYygxLDMsNiksIA0KICAgICAgICAgICAga2V5ID0gIkZhY3RvciIsIA0KICAgICAgICAgICAgdmFsdWUgPSAidmFsdWUiKSU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeSA9IE9QX2JpbikpKw0KICBnZW9tX3Ntb290aChhZXMoZmlsbCA9IFByb3RvY29sLCANCiAgICAgICAgICAgICAgICAgIGNvbCA9IFByb3RvY29sKSwNCiAgICAgICAgICAgICAgbWV0aG9kID0gImdsbSIsIA0KICAgICAgICAgICAgICBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksDQogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxzLCBuYW1lID0gIlByb3RvY29sIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFscywgbmFtZSA9ICJQcm90b2NvbCIpICsNCiAgZmFjZXRfd3JhcCh+RmFjdG9yLCBuY29sPTEsIHNjYWxlcyA9ICJmcmVlX3giKSsNCiAgbGFicyh5PSJCaW5hcnkgT1AiLCB4ID0gTlVMTCkgKyANCiAgdGhlbWVfYncoMTApDQoNCnA1ID0gZGYlPiVnYXRoZXIoYygxLDMsNiksIA0KICAgICAgICAgICAga2V5ID0gIkZhY3RvciIsIA0KICAgICAgICAgICAgdmFsdWUgPSAidmFsdWUiKSU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeSA9IE9QX3JhdGUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGZpbGwgPSBQcm90b2NvbCwgDQogICAgICAgICAgICAgICAgICBjb2wgPSBQcm90b2NvbCksDQogICAgICAgICAgICAgIG1ldGhvZCA9ICJnbG0iLCANCiAgICAgICAgICAgICAgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJxdWFzaWJpbm9taWFsIikpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEpKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFscywgbmFtZSA9ICJQcm90b2NvbCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbHMsIG5hbWUgPSAiUHJvdG9jb2wiKSArDQogIGZhY2V0X3dyYXAofkZhY3RvciwgbmNvbD0xLCBzY2FsZXMgPSAiZnJlZV94IikrDQogIGxhYnMoeT0iT1AgcmF0ZSIsIHggPSBOVUxMKSArIA0KICB0aGVtZV9idygxMCkNCg0KcDYgPSBkZiU+JW11dGF0ZShvZGQgPSBPUF9yYXRlLygxLU9QX3JhdGUpKSU+JQ0KICBnYXRoZXIoYygxLDMsNiksDQogICAgICAgICAgICBrZXkgPSAiRmFjdG9yIiwgDQogICAgICAgICAgICB2YWx1ZSA9ICJ2YWx1ZSIpJT4lDQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCB5ID0gb2RkKSkrDQogIGdlb21fc21vb3RoKGFlcyhmaWxsID0gUHJvdG9jb2wsIA0KICAgICAgICAgICAgICAgICAgY29sID0gUHJvdG9jb2wpLA0KICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xtIiwNCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFscywgbmFtZSA9ICJQcm90b2NvbCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbHMsIG5hbWUgPSAiUHJvdG9jb2wiKSArDQogIGZhY2V0X3dyYXAofkZhY3RvciwgbmNvbD0xLCBzY2FsZXMgPSAiZnJlZV94IikrDQogIGxhYnMoeT0iT2RkcyA9IHAxL3AwIiwgeCA9IE5VTEwpICsgDQogIHRoZW1lX2J3KDEwKQ0KICAgICAgICAgICAgICAgIA0KcDQrcDYrcDUNCmBgYA0KDQpDaMO6IHRow61jaDogSMOsbmggdHLDqm4gZ+G7k20gMyBj4buZdCwgdOG7qyB0csOhaSBzYW5nIHBo4bqjaSBs4bqnbiBsxrDhu6N0IHRyw6xuaCBiw6B5OiAoMSkgTGnDqm4gaOG7hyBnaeG7r2EgZ2nDoSB0cuG7iyBBRkMsIEFnZSwgVGhpY2tuZXNzICgzIGjDoG5nLCB0cuG7pWMgWCkgdsOgIGto4bqjIG7Eg25nIMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gKHRy4bulYyBZKTsgKDIpIExpw6puIGjhu4cgZ2nhu69hIGdpw6EgdHLhu4sgQUZDLCBBZ2UsIFRoaWNrbmVzcyAoMyBow6BuZywgdHLhu6VjIFgpIHbDoCBPZGRzICh04buJIHPhu5EgZ2nhu69hIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZy90aOG6pXQgYuG6oWkpOyB2w6AgKDMpIExpw6puIGjhu4cgZ2nhu69hIGdpw6EgdHLhu4sgQUZDLCBBZ2UsIFRoaWNrbmVzcyAoMyBow6BuZywgdHLhu6VjIFgpIHbDoCB04bu3IGzhu4cgdGhhaSBkaeG7hW4gdGnhur9uIHRyw6puIHThu5VuZyBz4buRIHBow7RpIGNodXnhu4NuICh0cuG7pWMgWSkuIEThu68gbGnhu4d1IMSRxrDhu6NjIHBow6JuIHRow6BuaCAyIG5ow7NtOiBwaMOhYyDEkeG7kyBQUE9TIChtw6B1IGjhu5NuZykgdsOgIEduUmhhIChtw6B1IHhhbmgpLg0KDQpIw6xuaCDhuqNuaCBuw6B5IGfhu6NpIMO9IG3hu5l0IHPhu5EgZ2nhuqMgdGh1eeG6v3QgbmjGsCBzYXU6DQoNCjEpIEFGQyBjw7MgaGnhu4d1IOG7qW5nIHTGsMahbmcgdMOhYyBnaeG7r2EgQUZDIHbDoCBsb+G6oWkgcGjDoWMgxJHhu5MsIHbDrCAyIMSR4buTIHRo4buLIGPDoWMgaMOgbSB0dXnhur9uIHTDrW5oL2xvZ2lzdGljIGPhu6dhIDIgbmjDs20gY+G6r3QgbmhhdSwgduG7m2kgaGnhu4d1IOG7qW5nIEFGQyBraMOhYyBiaeG7h3Qg4bufIG3hu5dpIG5ow7NtLg0KDQoyKSBUdeG7lWkgY8OzIGhp4buHdSDhu6luZyDEkeG7mWMgbOG6rXAgdsOgIMSR4buTbmcgbmjhuqV0IOG7nyAyIHBow6FjIMSR4buTLCBraOG6oyBuxINuZyB0aGFpIGRp4buFbiB0aeG6v24gdMawxqFuZyBxdWFuIG5naOG7i2NoIHbhu5tpIHR14buVaSAoZ2nhuqNtIGThuqduIOG7nyBwaOG7pSBu4buvIGzhu5tuIHR14buVaSk7DQoNCjMpIMSQ4buZIGTDoHkgbuG7mWkgbeG6oWMgdOG7rSBjdW5nIGPDsyBoaeG7h3Ug4bupbmcgxJHhu5ljIGzhuq1wIHbhu5tpIHBow6FjIMSR4buTLCB2w6AgxJHhu5NuZyBuaOG6pXQgZ2nhu69hIDIgbmjDs20gcGjDoWMgxJHhu5MsIGhp4buHdSDhu6luZyBuw6B5IHLhuqV0IHnhur91IHbDoCBjw7Mga2h1eW5oIGjGsOG7m25nIHTGsMahbmcgcXVhbiB0aHXhuq1uIHbhu5tpIGto4bqjIG7Eg25nIHRoYWkgZGnhu4VuIHRp4bq/bi4NCg0KIyBMw70gdGh1eeG6v3QgduG7gSBtw7QgaMOsbmggaOG7k2kgcXV5IGxvZ2lzdGljDQoNCkNobyBiw6BpIHRvw6FuIHRo4buRbmcga8OqIGhp4buHbiB0aOG7nWksIHRhIGPhuqduIG3hu5l0IHBoxrDGoW5nIHRp4buHbiBjaG8gcGjDqXAgbGnDqm4ga+G6v3QgZ2nDoSB0cuG7iyBj4bunYSBt4buZdCBiaeG6v24gxJHhu5ljIGzhuq1wICh54bq/dSB04buRIHRpw6puIGzGsOG7o25nKSAkWCQgdsOgIG3hu5l0IHjDoWMgc3XhuqV0ICRZID0gcCBcaW4gWzAsMV0kLiBDw7RuZyBj4bulIMSRw7MgbMOgIG3DtCBow6xuaCBsb2dpc3RpYy4NCg0KTcO0IGjDrG5oIGjhu5NpIHF1eSBsb2dpc3RpYyBjw7MgbeG7mXQgbOG7i2NoIHPhu60gbMOidSDEkeG7nWkgKGtob+G6o25nIDc4IG7Eg20pLCB0csaw4bubYyBj4bqjIHPhu7EgcmEgxJHhu51pIGPhu6dhIGzDvSB0aHV54bq/dCB24buBIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCBHTE0uIEPDonUgY2h1eeG7h24gbsOgeSBraMOhIGTDoGksIHbDoCBz4buxIGto4bufaSDEkeG6p3UgY+G7p2EgbsOzIGtow7RuZyBsacOqbiBxdWFuIGfDrCDEkeG6v24geMOhYyBzdeG6pXQgdsOgIHRo4buRbmcga8OqLiBDaMO6bmcgdGEgc+G6vSBraOG7n2kgaMOgbmggeGEgaMahbiB04burIGNo4buXIG5o4bqtbiByYSB0cm9uZyB0aOG6vyBnaeG7m2kgdOG7sSBuaGnDqm4gbHXDtG4gaGnhu4duIGRp4buHbiBt4buZdCBxdXkgbHXhuq10IHRp4bq/biB0cmnhu4NuIMSR4bq3YyBiaeG7h3QgZ+G7k20gMyBnaWFpIMSRb+G6oW46IDEpIEdpYWkgxJFv4bqhbiBz4bubbSB24bubaSBz4buxIHTEg25nIHRyxrDhu59uZyBsxal5IHRp4bq/biwgeOG6pXAgeOG7iSBow6BtIG3FqSAoZXhwb25lbnRpYWwpOyAyKSBUaeG6v3AgdGhlbyBsw6AgZ2lhaSDEkW/huqFuIGLDo28gaMOyYTogdOG7kWMgxJHhu5kgcGjDoXQgdHJp4buDbiBjaOG6rW0gbOG6oWksIHR1eeG6v24gdMOtbmg7IDMpIEN14buRaSBjw7luZyBsw6AgZ2lhaSDEkW/huqFuIOG7lW4gxJHhu4tuaCB2w6AgdHLGsOG7n25nIHRow6BuaDogc+G7sSBwaMOhdCB0cmnhu4NuIG5n4burbmcgbOG6oWkuIEtoaSBiaeG7g3UgZGnhu4VuIHRp4bq/biB0csOsbmggbsOgeSB0aGVvIHRo4budaSBnaWFuLCBz4bq9IHThuqFvIHJhIG3hu5l0IMSRxrDhu51uZyBjb25nIGjDrG5oIGNo4buvIFMgKHNpZ21vaWQgY3VydmUpLg0KDQpUYSBjw7MgdGjhu4MgcXVhbiBzw6F0IHRo4bqleSBxdXkgbHXhuq10IG7DoHkg4bufIG3hu41pIGzEqW5oIHbhu7FjOiB0xINuZyB0csaw4bufbmcgZMOibiBz4buRL2tpbmggdOG6vywgY2h1IHRyw6xuaCBjaHV54buDbiBow7NhIHNpbmggaOG7jWMsIHBo4bqjbiDhu6luZyBow7NhIGjhu41jLiBUcm9uZyBT4bqjbiBQaOG7pSBraG9hLCB0YSBjw7MgdGjhu4MgbmjDrG4gdGjhuqV5IG7DsyB0cm9uZyBkaeG7hW4gdGnhur9uIGPDoWMgaG9ybW9uZSBzaW5oIGThu6VjLCBz4buxIHTEg25nIHRyxrDhu59uZyBj4bunYSBiw6BvIHRoYWkgdHJvbmcgdOG7rSBjdW5nIG5nxrDhu51pIG3hurksIGxpw6puIGjhu4cgbGnhu4F1IC8gxJHDoXAg4bupbmcgdHJvbmcgZMaw4bujYyBs4buxYyBo4buNYy4uLg0KDQpU4burIHRo4bq/IGvhu4kgMTgtMTksIG5o4buvbmcgbmjDoCB0b8OhbiBo4buNYyDEkcOjIGto4bqjbyBzw6F0IHF1eSBsdeG6rXQgbsOgeSB2w6AgbOG6rXAgcmEgY8OhYyBow6BtIHRvw6FuIGjhu41jIGNobyBwaMOpcCB0w6FpIGhp4buHbiDEkcaw4budbmcgY29uZyBow6xuaCBjaOG7ryBTLCBn4buNaSBsw6AgY8OhYyBow6BtIHNpZ21vaWQsIHRow60gZOG7pSBow6BtIGh5cGVyYm9saWMgdGFuZ2VudCBj4bunYSBKZWFuIFNhdXJyeSBuxINtIDE3NzQuDQoNClRyb25nIHPhu5EgY8OhYyBow6BtIHNpZ21vaWQsIHRhIGPDsyBow6BtIGxvZ2lzdGljIGzDoCBuaMOibiB24bqtdCBjaMOtbmggdHJvbmcgY8OidSBjaHV54buHbiBuw6B5LiBIw6BtIGxvZ2lzdGljIGRvIG5ow6AgdG/DoW4gaOG7jWMgbmfGsOG7nWkgUGjDoXAgUGllcnJlIEZyYW7Dp29pcyBWZXJodWxzdCB0aGnhur90IGzhuq1wIHbDoG8gbsSDbSAxODQ1LCB24bubaSBjw7RuZyBk4bulbmcgbmd1ecOqbiB0aOG7p3kgbmjhurFtIGto4bqjbyBzw6F0IHF1eSBsdeG6rXQgdMSDbmcgdHLGsOG7n25nIGTDom4gc+G7kSB0cm9uZyBt4buZdCBxdeG6p24gdGjhu4MuIEPDtG5nIHRo4bupYyBj4bunYSBow6BtIGxvZ2lzdGljIMSRxrDhu6NjIHRyw6xuaCBiw6B5IG5oxrAgc2F1Lg0KDQokJHkgPSBcZnJhY3tlXnt4fX17MSArIGVee3h9fSA9IFxmcmFjezF9ezEgKyBlXnsteH19ID0gXGZyYWN7MX17Mn0gKyBcZnJhY3sxfXsyfSB0YW5oKFxmcmFje3h9ezJ9KSQkDQoNCkjDrG5oOiDEkeG7kyB0aOG7iyBow6BtIGxvZ2lzdGljLiBU4burIGdpw6EgdHLhu4sgxJHhuqd1IHbDoG8gbMOgIHPhu5EgdGjhu7FjICR4JCBi4bqldCBrw6wsIGjDoG0gbG9naXN0aWMgeHXhuqV0IGvhur90IHF14bqjIGzDoCBt4buZdCBnacOhIHRy4buLIHRyb25nIGtob+G6o25nICgwLDEpLiANCg0KYGBge3IsZWNobz1GfQ0KeCA9IHNlcSgtNCw0LDAuMDEpDQp5ID0gMC41ICsgMC41KnRhbmgoeCkNCg0KcXBsb3QoeCx5LCANCiAgICAgIGdlb20gPSAibGluZSIsIA0KICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBsYWJzKHRpdGxlID0gImxvZ2lzdGljIGZ1bmN0aW9uOiB5ID0gMS8oMSArIGV4cCgteCkpIiwNCiAgICAgIHkgPSAieSA9IDEvKDEgKyBleHAoLXgpIikrDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoLTQsNCwxKSkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpN4buZdCBjw6FjaCB0w6xuaCBj4budLCDEkeG6t2MgdMOtbmggbsOgeSBj4bunYSBow6BtIGxvZ2lzdGljIGThuqtuIMSR4bq/biBt4buZdCDhu6luZyBk4bulbmcgcXVhbiB0cuG7jW5nIHRyb25nIGzEqW5oIHbhu7FjIHRo4buRbmcga8OqOiBuw7MgZ2nhuqNpIHF1eeG6v3Qgbmh1IGPhuqd1IGxpw6puIGvhur90IGdpw6EgdHLhu4sgbeG7mXQgdOG6rXAgYmnhur9uICRYJCB2w6AgeMOhYyBzdeG6pXQgcXVhbiBzw6F0IMSRxrDhu6NjIGdpw6EgdHLhu4sgJFk9MSQgKFkgbMOgIG3hu5l0IGJp4bq/biBuaOG7iyBnacOhKS4NCg0KTcO0IGjDrG5oIGxvZ2lzdGljIGPhu5UgxJFp4buDbiDEkcaw4bujYyDEkeG7i25oIG5naMSpYSB0cuG7sWMgdGnhur9wIGThu7FhIHbDoG8gaMOgbSBsb2dpc3RpYywgYuG6sW5nIGPDoWNoIHRoYXkgxJHhu5FpIHPhu5EgJHgkIGLhurFuZyBt4buZdCDEkeG6oWkgbMaw4bujbmcgJGckIG5oxrAgc2F1Og0KDQoNCiQkUHIoWT0xfFgpIFxzaW0gbG9naXN0aWMoWCkgPSBcZnJhY3sxfXsxICsgZV57LWd9fSQkDQoNClbhu5tpICRnJCBsw6AgbeG7mXQgYmnhu4N1IHRo4bupYyAoaMOgbSkgdHV54bq/biB0w61uaCDEkcaw4bujYyBtw7QgaMOsbmggaMOzYSB0aGVvIHThuq1wIGJp4bq/biAkWCQNCg0KJCRnID0gXGJldGFfMCArIFxiZXRhXzFYXzEgKyBcYmV0YV8yWF8yICsgLi4uICsgXGJldGFfalhfaiQkDQpUdXkgbmhpw6puLCBwaGnDqm4gYuG6o24gdGjhu7FjIGThu6VuZyBj4bunYSBtw7QgaMOsbmggaOG7k2kgcXV5IGxvZ2lzdGljIGNo4buJIMSRxrDhu6NjIMSR4buLbmggaMOsbmggdsOgbyBuxINtIDE5NDQsIGtoaSBuaMOgIHRo4buRbmcga8OqIEpvc2VwaCBCZXJrc29uIHRoaeG6v3QgbOG6rXAgbeG7mXQga2jDoWkgbmnhu4dtIHF1YW4gdHLhu41uZyBsw6AgTG9nIG9kZHMgdsOgIGTDuW5nIG7DsyBsw6BtIHRoYW5nIMSRbyBjaG8gYsOgaSB0b8OhbiDGsOG7m2MgbMaw4bujbmcgYmnhur9uIG5o4buLIGdpw6EuDQoNCk5oxrAgdGEgYmnhur90LCBPZGRzIGzDoCB04buJIHPhu5EgZ2nhu69hIDIgeMOhYyBzdeG6pXQuIFbhu5tpICRwJCBsw6AgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nICgkWT0xJCkgdsOgICQxLXAkIGzDoCB4w6FjIHN14bqldCB0aOG6pXQgYuG6oWkgKCRZPTAkKSwgdGEgY8OzOg0KDQokJE9kZHMgPSBcZnJhY3twfXsxLXB9JCQNCg0KVOG7qyDEkcOzLCB0YSBjw7MgaMOgbSBsb2dpdCBsw6AgbeG7mXQgY8O0bmcgY+G7pSBraMOhYyBjaG8gcGjDqXAgbGnDqm4ga+G6v3QgdHLhu7FjIHRp4bq/cCB04bqtcCBiaeG6v24gJFgkIHbDoCBnacOhIHRy4buLIHjDoWMgc3XhuqV0ICRZPXAkLiBIw6BtIGxvZ2l0IGNow61uaCBsw6Agc+G7sSDDoXAgZOG7pW5nIGjDoG0gbG9nYXJpdCB04buxIG5oacOqbiBjaG8gT2RkczoNCg0KJCRsb2dpdChwKSA9IGxvZyhcZnJhY3twfXsxLXB9KSQkDQoNClRhIGThu4UgZMOgbmcgbmjhuq1uIHRo4bqleSBt4buRaSBsacOqbiBo4buHIG5naOG7i2NoIMSR4bqjbyBnaeG7r2EgMiBow6BtIGxvZ2l0IHbDoCBow6BtIGxvZ2lzdGljIGPhu5UgxJFp4buDbjogDQoNCmBgYHtyLCBlY2hvID0gRn0NCnAgPSBzZXEoMCwxLDAuMDEpDQp5ID0gbG9nKHAvKDEtcCkpDQoNCnAxID0gcXBsb3QocCx5LCANCiAgICAgIGdlb20gPSAibGluZSIsIA0KICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBsYWJzKHRpdGxlID0gImxvZ2l0IGZ1bmN0aW9uOiB5PWxvZyhwLygxLXApKSIsIA0KICAgICAgIHkgPSAibG9naXQocCkgPSBsb2cob2RkcykgPSBsb2cocC8oMS1wKSIpKw0KICB0aGVtZV9idygpDQoNCnAyID0gcXBsb3QoeSxwLCANCiAgICAgIGdlb20gPSAibGluZSIsIA0KICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBsYWJzKHRpdGxlID0gImxvZ2lzdGljIGZ1bmN0aW9uIiwgDQogICAgICAgeCA9ICJsb2dpdChwKSIsDQogICAgICAgeSA9ICJwIikrDQogIHRoZW1lX2J3KCkNCg0KcDEgKyBwMg0KYGBgDQoNClThu6sgxJHDsywgQmVya3NvbiDEkcOjIGNodeG6qW4gaMOzYSBo4buTaSBxdXkgbG9naXN0aWMgdGjDoG5oIG3hu5l0IHBoxrDGoW5nIHBow6FwIHRo4buRbmcga8OqIHF1eSDGsOG7m2MsIHPhu60gZOG7pW5nIGjDoG0gbG9naXQgdGhheSB2w6wgbG9naXN0aWMsIHbDoCBkw7luZyBsb2cob2RkcykgbMOgbSB0aGFuZyDEkW8sIMSRxqFuIHbhu4sgY2hvIGJp4bq/biBr4bq/dCBxdeG6oyAkWSQgdHJvbmcgbcO0IGjDrG5oLiBTYXUgxJHDsywgRGF2aWQgQ294ICgxOTU4KSBob8OgbiB0aGnhu4duIGvhu7kgdGh14bqtdCDGsOG7m2MgbMaw4bujbmcgdGhhbSBz4buRIHRyb25nIG3DtCBow6xuaCBsb2dpc3RpYy4NCg0KU2F1IGPDuW5nLCB2w6BvIG7Eg20gMTk3MiBOZWxkZXIgdsOgIFdlZGRlcmJ1cm4gxJHDoyBo4bujcCBuaOG6pXQgbcO0IGjDrG5oIGxvZ2lzdGljIHbDoG8gaOG7hyB0aOG7kW5nIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgKEdMTSksIG3DtCBow6xuaCBsb2dpc3RpYyBsw6AgbeG7mXQgdGjhu4MgxJHhurdjIGJp4buHdCBj4bunYSBtw7QgaMOsbmggR0xNLCDGsOG7m2MgbMaw4bujbmcgZ2nDoSB0cuG7iyBj4bunYSAkXG11JCBsw6AgdHJ1bmcgYsOsbmggY+G7p2EgYmnhur9uIGvhur90IHF14bqjICRZJCB0aGVvIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIEJpbm9taWFsIHbDoCAyIHRoYW0gc+G7kSAkQkkobixcbXUpJCANCg0KJCRQcihZPXl8bixcbXUpID0gXGZyYWN7biF9e3khKG4teSkhfVxtdV57eX0oMS1wKV57bi15fSQkDQoNClRhIMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIGPhu6dhICRcbXUkIGLhurFuZyBt4buZdCBtw7QgaMOsbmggdHV54bq/biB0w61uaCB24bubaSBow6BtIGxpw6puIGvhur90IGxvZ2l0Og0KDQokJGxvZ2l0KFxtdSkgPSBsb2coXGZyYWN7XG11fXsxLVxtdX0pIFxzaW0gXGJldGFfMCArIFxiZXRhXzFYXzEgKyBcYmV0YV8yWF8yICsgLi4uICsgXGJldGFfalhfaiAgJCQNClRhIHRo4bqleSwgxJHhu4tuaCBuZ2jEqWEgbcO0IGjDrG5oIGjhu5NpIHF1eSBsb2dpc3RpYyB0aGVvIGzDvSB0aHV54bq/dCBHTE0gdsOgIGjDoG0gbG9naXQgZOG7hSBoaeG7g3UgdsOgIMSRxqFuIGdp4bqjbiBoxqFuIG5oaeG7gXUgc28gduG7m2kgxJHhu4tuaCBuZ2jEqWEgbmd1ecOqbiB0aOG7p3kgZOG7sWEgdsOgbyBow6BtIGxvZ2lzdGljLiBOaMawbmcgdsOsIGzDvSBkbyBs4buLY2ggc+G7rSwgdGEgduG6q24gZMO5bmcgdMOqbiBn4buNaSAibG9naXN0aWMgcmVncmVzc2lvbiIgY2hvIGxv4bqhaSBtw7QgaMOsbmggbsOgeSwgbmjGsG5nIHRhIGhp4buDdSBi4bqjbiBjaOG6pXQgY+G7p2EgbsOzIGNo4buJIMSRxqFuIGdp4bqjbiBsw6AgbeG7mXQgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgQmlub21pYWwgaG/hurdjIEJlcm5vdWxsaSAodHLGsOG7nW5nIGjhu6NwIMSR4bq3YyBiaeG7h3Qga2hpICRuPTEkKQ0KDQojIELGsOG7m2MgMkE6IE3DtCBow6xuaCBo4buTaSBxdXkgTG9naXN0aWMgY2hvIGJp4bq/biBr4bq/dCBxdeG6oyBuaOG7iyBnacOhDQoNClRhIHPhur0gbOG6p24gbMaw4bujdCBwaMOibiB0w61jaCAyIG3DtCBow6xuaCBsb2dpc3RpYyBraMOhYyBuaGF1IGNobyAyIGJp4bq/biBr4bq/dCBxdeG6ozogMSkga+G6v3QgcXXhuqMgbmjhu4sgZ2nDoSAodGjDoG5oIGPDtG5nIGhv4bq3YyB0aOG6pXQgYuG6oWkgdHV54buHdCDEkeG7kWkgduG7gSB0aGFpIGRp4buFbiB0aeG6v24gdHJvbmcgdGjDrSBuZ2hp4buHbSwgYmnhur9uIE9QX2JpbikgdsOgIDIpIHThu7cgbOG7hyB0aGFpIGRp4buFbiB0aeG6v24gdHLDqm4gdOG7lW5nIHPhu5EgcGjDtGkgY2h1eeG7g24gKGJp4bq/biBPUF9yYXRlKS4NCg0KQ2hvIGvhur90IHF14bqjIHRo4bupIG5o4bqldCwgbcO0IGjDrG5oIMOhcCBk4bulbmcgbeG7mXQgdHLGsOG7nW5nIGjhu6NwIMSR4bq3YyBiaeG7h3QgY+G7p2EgcGjDom4gcGjhu5FpIEJpbm9taWFsIHbhu5tpICRuPTEkLCB24bubaSDEkcahbiB24buLIHF1YW4gc8OhdCBsw6AgY8OhIHRo4buDIGLhu4duaCBuaMOibi4gTeG7pWMgdGnDqnUgY+G7p2EgbcO0IGjDrG5oIGzDoCDGsOG7m2MgbMaw4bujbmcgeMOhYyBzdeG6pXQgcXVhbiBzw6F0IMSRxrDhu6NjIGdpw6EgdHLhu4sgJE9QXF9iaW4gPSAxJCBjaG8gbeG7l2kgY8OhIHRo4buDLiANCg0KTcO0IGjDrG5oIGPDsyBjw7RuZyB0aOG7qWM6IE9QX2JpbiB+ICBQcm90b2NvbCpBRkMgKyBUaGlja25lc3MgKyBBZ2U7IGZhbWlseSA9ICJiaW5vbWlhbCIgKGjDoG0gbGnDqm4ga+G6v3QgbeG6t2MgxJHhu4tuaCBsw6AgbG9naXQpLg0KDQpO4buZaSBkdW5nIGPhu6dhIG3DtCBow6xuaCBuaMawIHNhdToNCg0KYGBge3J9DQpsb2dfbW9kID0gZ2xtKE9QX2JpbiB+ICBQcm90b2NvbCpBRkMgKyBUaGlja25lc3MgKyBBZ2UsDQogICAgICAgICAgICAgICAgZGF0YSA9IGRmLA0KICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCnN1bW1hcnkobG9nX21vZCkNCmBgYA0KRGnhu4VuIGdp4bqjaSBu4buZaSBkdW5nIGvhur90IHF14bqjIHRow7QgY+G7p2EgbcO0IGjDrG5oOg0KDQpEbyBz4butIGThu6VuZyBow6BtIGxpw6puIGvhur90IGzDoCBsb2dpdCwgZ2nDoSB0cuG7iyDGsOG7m2MgbMaw4bujbmcgY+G7p2EgbcO0IGjDrG5oIGhp4buHbiB0aOG7nWkgbMOgICRsb2dpdChcbXUpJCBoYXkgJGxvZyhcZnJhY3tcbXV9ezEtXG11fSkkIGhheSAkbG9nKE9kZHMpJCwgduG7m2kgJFxtdSQgbMOgIHjDoWMgc3XhuqV0IMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gdHJ1bmcgYsOsbmggKGNobyBt4buVaSBjw6EgdGjhu4MpLiBU4bqldCBj4bqjIGjhu4cgc+G7kSBo4buTaSBxdXkgxJFhbmcg4bufIHRoYW5nIMSRbyBsb2cob2RkcykuIFRhIGPDsyB0aOG7gyBjaHV54buDbiB24buBIHRoYW5nIMSRbyB4w6FjIHN14bqldCBi4bqxbmcgaMOgbSBwbG9naXMsIGhv4bq3YyB24buBIHRoYW5nIMSRbyBPZGRzIGLhurFuZyBow6BtIGV4cG9uZW50aWFsLg0KDQokSW50ZXJjZXB0ID0gMi4yODIyMCQgdMawxqFuZyDhu6luZyB24bubaSBsb2cob2RkKSBj4bunYSB4w6FjIHN14bqldCB0aGFpIGRp4buDbiB0aeG6v24g4bufIHBow6FjIMSR4buTIHRoYW0gY2hp4bq/dSAoR25SSF9hbnQpLCB0cm9uZyDEkWnhu4F1IGtp4buHbiBnacOhIHRy4buLIEFnZSwgQUZDIHbDoCBUaGlja25lc3MgZ2nhu68gY+G7kSDEkeG7i25oIOG7nyBt4bupYyB0cnVuZyBiw6xuaCBj4bunYSBxdeG6p24gdGjhu4MuDQoNCiRQcm90b2NvbFBQT1MgPSAzLjU2OTUyJCB0xrDGoW5nIOG7qW5nIHbhu5tpIHPhu7EgdGhheSDEkeG7lWkgbG9nKG9kZCkgY+G7p2EgeMOhYyBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uIOG7nyBwaMOhYyDEkeG7kyBQUE9TIHNvIHbhu5tpIG5ow7NtIMSR4buRaSBjaOG7qW5nIChHblJIX2FudCksIHRyb25nIMSRaeG7gXUga2nhu4duIGdpw6EgdHLhu4sgQWdlLCBBRkMgdsOgIFRoaWNrbmVzcyBraMO0bmcgxJHhu5VpLg0KDQpDw6FjIGjhu4cgc+G7kSBo4buRaSBxdXkgY2hvIEFGQyxBZ2UgdsOgIFRoaWNrbmVzcyBs4bqnbiBsxrDhu6N0IOG7qW5nIHbhu5tpIHPhu7EgdGhheSDEkeG7lWkgY+G7p2EgbG9nKG9kZCkgeMOhYyBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uIGtoaSBBRkMsIEFnZSBob+G6t2MgVGhpY2tuZXNzIHTEg25nIDEgxJHGoW4gduG7iy4NCg0KVsOsIG3DtCBow6xuaCBjw7MgeMOpdCBoaeG7h3Ug4bupbmcgdMawxqFuZyB0w6FjIGdp4buvYSBwaMOhYyDEkeG7kyBQUE9TIHbDoCBBRkMsIHRhIGPDsyB0aMOqbSBo4buHIHPhu5EgaOG7k2kgcXV5IFByb3RvY29sUFBPUzpBRkM7IHbhu5tpIMO9IG5naMSpYSDEkW8gbMaw4budbmcgc+G7sSB0aGF5IMSR4buVaSBj4bunYSBsb2cob2RkKSBj4bunYSB4w6FjIHN14bqldCB0aGFpIGRp4buFbiB0aeG6v24ga2hpIEFGQyB0xINuZyAxIMSRxqFuIHbhu4sgdsOgIGtoaSBz4butIGThu6VuZyBwaMOhYyDEkeG7kyBQUE9TLg0KDQpN4buZdCBjw6FjaCDEkcahbiBnaeG6o24sIHRhIGPDsyB0aOG7gyB4w6l0IGThuqV1IGPhu6dhIGPDoWMgaOG7hyBz4buRIGjhu5NpIHF1eSBuw6B5IMSR4buDIGjDrG5oIGR1bmcgduG7gSBoaeG7h3Ug4bupbmcgbcOgIGPDoWMgYmnhur9uIGfDonkgcmEgY2hvIHjDoWMgc3XhuqV0IGPhu6dhIHRoYWkgZGnhu4VuIHRp4bq/bi4gTeG7mXQgaOG7hyBz4buRIGjhu5NpIHF1eSA+IDAgY2hvIHRo4bqleSBt4buRaSB0xrDGoW5nIHF1YW4gdGh14bqtbiAobMOgbSB0xINuZyksIG5nxrDhu6NjIGzhuqFpIG3hu5l0IGjhu4cgc+G7kSBo4buTaSBxdXkgPDAgY2hvIHRo4bqleSB0xrDGoW5nIHF1YW4gbmdo4buLY2ggKGzDoG0gZ2nhuqNtKS4NCg0KUHIoPnx6fCkgdHLDrG5oIGLDoHkgZ2nDoSB0cuG7iyBwIGPhu6dhIGtp4buDbSDEkeG7i25oIFdhbGQsIG5o4bqxbSBwaOG7pyBuaOG6rW4gZ2nhuqMgdGh1eeG6v3QgdsO0IGhp4buHdSBsw6AgbeG7mXQgYmnhur9uIGtow7RuZyBnw6J5IHJhIGhp4buHdSDhu6luZyBuw6BvIGPhuqMgKGtow7RuZyBjw7MgbGnDqm4gaOG7hykgxJHhu5FpIHbhu5tpIHhhYyBzdeG6pXQgT1AgKGjhu4cgc+G7kSBo4buTaSBxdXkgPSAwKS4gDQoNClR1eSBuaGnDqm4sIHLhuqV0IGtow7MgxJHhu4MgdGjhu7FjIGhp4buHbiBzdXkgZGnhu4VuIHRo4buRbmcga8OqIHRoZW8gY8OhY2ggbsOgeSwgbsOqbiB0YSBz4bq9IHRoZW8gY29uIMSRxrDhu51uZyDGsOG7m2MgdMOtbmggbWFyZ2luYWwgZWZmZWN0cyAoaGnhu4d1IOG7qW5nIGPhuq1uIGJpw6puKSBjaG8gY8OhYyB54bq/dSB04buRLiANCg0KR2nhuqMgxJHhu4tuaCB0YSBtdeG7kW4ga2jhuqNvIHPDoXQgaGnhu4d1IOG7qW5nIGPhu6dhIG3hu5l0IGJp4bq/biDEkeG7i25oIHTDrW5oIGPDsyAyIGLhuq1jIGdpw6EgdHLhu4sgKHBow6FjIMSR4buTIFBQT1MgaG/hurdjIEduUkgpLCBtw7QgaMOsbmggc+G6vSDGsOG7m2MgbMaw4bujbmcgxJHGsOG7o2MgMiBnacOhIHRy4buLIHjDoWMgc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biB0cnVuZyBiw6xuaCBjaG8gMiBuaMOzbSBuw6B5IGzDoCAkXG11X3tQUE9TfSQgdsOgICRcbXVfe0duUkh9JA0KDQpU4burIMSRw6J5LCB0YSBjw7MgdGjhu4MgxrDhu5tjIHTDrW5oIDMgbG/huqFpIG1hcmdpbmFsIGVmZmVjdHMgZ+G7k206DQoNCjEpIE1hcmdpbmFsIHJpc2sgZGlmZmVyZW5jZSAoUkQpDQoNClRy4buLIHPhu5EgUkQgxJFvIGzGsOG7nW5nIHRy4buxYyB0aeG6v3Agc+G7sSB0aGF5IMSR4buVaS9raMOhYyBiaeG7h3QvdMawxqFuZyBwaOG6o24gduG7gSB4w6FjIHN14bqldCBnaeG7r2EgUFBPUyB2w6AgcGjDoWMgxJHhu5MgdGhhbSBjaGnhur91DQoNCiQkUkQgPSBcbXVfe1BQT1N9IC0gIFxtdV97R25SSGFudH0kJA0KUkQgY2hvIGJp4bq/dCBraGkgZMO5bmcgcGjDoWMgxJHhu5MgUFBPUywgeMOhYyBzdeG6pXQgxJHhuqF0IHRoYXkgZGnhu4VuIHRp4bq/biBz4bq9IHRoYXkgxJHhu5VpIGJhbyBuaGnDqnUgc28gduG7m2kgcGjDoWMgxJHhu5MgdGhhbSBjaGnhur91LiBWw6wgYuG6o24gY2jhuqV0IGPhu6dhIFJEIGzDoCBoaeG7h3Ugc+G7kSBnaeG7r2EgMiB4w6FjIHN14bqldCwgbsOzIGNo4buJIGPDsyB0aOG7gyBkYW8gxJHhu5luZyB0cm9uZyBraG/huqNuZyAkWy0xLDFdJCwgUkQgPSAwIGNobyB0aOG6pXkga2jDtG5nIGPDsyBraMOhYyBiaeG7h3QgKHTDrW5oIHTGsMahbmcgxJHGsMahbmcpLCBSRCA8IDAgaG/hurdjID4gMCBjaG8gcGjDqXAgeMOhYyDEkeG7i25oIGhp4buHdSDhu6luZyBj4bunYSBwaMOhYyDEkeG7kyBuw6BvIGNhbyBoxqFuL3Ro4bqlcCBoxqFuLg0KDQpgYGB7cn0NCnJkID0gYXZnX2NvbXBhcmlzb25zKA0KICBsb2dfbW9kLA0KICB2YXJpYWJsZXMgPSAiUHJvdG9jb2wiKSU+JQ0KICBkcGx5cjo6c2VsZWN0KC1jKDEsMiw1LDYpKSU+JQ0KICBtdXRhdGUoY29udHJhc3QgPSAiUkQiKQ0KDQpyZCAlPiUga25pdHI6OmthYmxlKCkNCmBgYA0KDQpL4bq/dCBxdeG6oyBjaG8gdGjhuqV5OiB0aGVvIMaw4bubYyB0w61uaCB0cnVuZyBiw6xuaCwgbmjDs20gYuG7h25oIG5ow6JuIGTDuW5nIHBow6FjIMSR4buTIFBQT1MgY8OzIHjDoWMgc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biB0xINuZyAwLjE0OCAobsOzaSBjw6FjaCBraMOhYzsgdOG7tyBs4buHIHRoYWkgZGnhu4VuIHRp4bq/biB0xINuZyAxNC44JSkgc28gduG7m2kgcGjDoWMgxJHhu5MgR25SSF9hbnQsIHPhu7EgZ2lhIHTEg25nIG7DoHkgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiAoa2hv4bqjbmcgdGluIGPhuq15IGtow7RuZyBjaOG7qWEgZ2nDoSB0cuG7iyAwLCBwID0gMC4wMzYpLg0KDQoyKSBNYXJnaW5hbCBPZGRzIHJhdGlvIChPUikNCg0KVHJvbmcgcGjhuqduIHRyw6puLCB0YSDEkcOjIGhp4buDdSB24buBIGLhuqNuIGNo4bqldCBj4bunYSBPZGRzIGzDoCBt4buZdCB04bu3IHPhu5EgZ2nhu69hIDIgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nL3Ro4bqldCBi4bqhaS4gT2RkcyByYXRpbyBsw6AgbeG7mXQgY8OhY2ggxJHhu4MgxJFvIGzGsOG7nW5nIHPhu7EgdMawxqFuZyBwaOG6o24gZ2nhu69hIDIgZ2nDoSB0cuG7iyBPZGRzIHTGsMahbmcg4bupbmcgduG7m2kgMiBi4bqtYyBnacOhIHRy4buLIGPhu6dhIGJp4bq/biBYICgyIHBow6FjIMSR4buTKSwgdGhlbyBjw7RuZyB0aOG7qWMgc2F1Og0KDQokJE9SID0gXGZyYWN7T2Rkc197UFBPU319e09kZHNfe0duUkhhfX0gPSBcZnJhY3tcbXVfe1BQT1N9LygxLVxtdV97UFBPU30pfXtcbXVfe0duUkhhfS8oMS1cbXVfe0duUkhhfSl9JCQNClbDrCBsw6AgMSB04bu3IHPhu5EsIE9SIGPDsyB0aOG7gyA+MSwgdsOgIGRhbyDEkeG7mW5nIHThu6sgMCDEkeG6v24gJCtcaW5mdHkkLiBHacOhIHRy4buLIE9SIDwgMSBjaG8gdGjhuqV5IGhp4buHdSDhu6luZyBj4bunYSBwaMOhYyDEkeG7kyBQUE9TIHRo4bqlcCBoxqFuIHNvIHbhu5tpIG5ow7NtIHRoYW0gY2hp4bq/dSwgbmfGsOG7o2MgbOG6oWkgT1IgPiAxIHbDoCBjw6BuZyBs4bubbiBjw6BuZyBjaG8gdGjhuqV5IHBow6FjIMSR4buTIFBQT1MgY8OzIGhp4buHdSBxdeG6oyBjYW8gaMahbi4gT1IgPSAxIGNobyB0aOG6pXkgMiBwaMOhYyDEkeG7kyBjw7MgaGnhu4d1IOG7qW5nIHTGsMahbmcgxJHGsMahbmcgbmhhdS4NCg0KMykgTWFyZ2luYWwgcmlzayByYXRpbyAoUlIpDQoNClJpc2sg4bufIMSRw6J5IHTGsMahbmcg4bupbmcgduG7m2kgw70gbmdoxKlhIHjDoWMgc3XhuqV0LCBraOG6oyBuxINuZywgdOG7tyBs4buHIHRow6BuaCBjw7RuZyAoZ2nDoSB0cuG7iyAkXG11JCBjaG8gbeG7l2kgcGjDoWMgxJHhu5MpLCByaXNrIHJhdGlvIMSRxqFuIGdp4bqjbiBsw6AgdOG7tyBz4buRIGdp4buvYSAyIHJpc2tzLg0KDQokJFJSID0gXGZyYWN7XG11X3tQUE9TfX17XG11X3tHblJIYX19ICQkDQpSUiDEkcaw4bujYyBkaeG7hW4gZ2nhuqNpIHTGsMahbmcgdOG7sSBuaMawIE9SLCBnacOhIHRy4buLIFJSPTEgY2hvIHRo4bqleSAyIHBow6FjIMSR4buTIGzDoCB0xrDGoW5nIMSRxrDGoW5nIG5oYXUsIGdpw6EgdHLhu4sgUlIgPiAxIGNobyB0aOG6pXkgcGjDoWMgxJHhu5MgUFBPUyBjw7MgaGnhu4d1IHF14bqjIGNhbyBoxqFuLCB2w6AgbmfGsOG7o2MgbOG6oWkgUlIgPCAxIGNobyB0aOG6pXkgcGjDoWMgxJHhu5MgR25SaGEgY8OzIGhp4buHdSBxdeG6oyBjYW8gaMahbi4NCg0KS+G6v3QgcXXhuqMgT1IgdsOgIFJSIMSRxrDhu6NjIHRyw6xuaCBiw6B5IHRyb25nIGLhuqNuZyBzYXU6DQoNCmBgYHtyfQ0Kb3IgPSBhdmdfY29tcGFyaXNvbnMoDQogIGxvZ19tb2QsDQogIHZhcmlhYmxlcyA9ICJQcm90b2NvbCIsDQogIHRyYW5zZm9ybV9wcmUgPSAibG5vcmF2ZyIsDQogIHRyYW5zZm9ybV9wb3N0ID0gImV4cCIpJT4lDQogIGRwbHlyOjpzZWxlY3QoLWMoMSwyLDg6MTApKSU+JQ0KICBtdXRhdGUoY29udHJhc3QgPSAiT1IiKQ0KDQpyciA9IGF2Z19jb21wYXJpc29ucygNCiAgbG9nX21vZCwNCiAgdmFyaWFibGVzID0gIlByb3RvY29sIiwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbnJhdGlvYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSBleHApJT4lZHBseXI6OnNlbGVjdCgtYygxLDIsODoxMCkpJT4lDQogICBtdXRhdGUoY29udHJhc3QgPSAiUlIiKQ0KDQpyYmluZChvcixycikgJT4lIGtuaXRyOjprYWJsZSgpDQpgYGANCg0KVGhlbyBr4bq/dCBxdeG6oyBuw6B5LCBj4bqjIGdpw6EgdHLhu4sgT1IgdsOgIFJSIMSR4buBdSBjaG8gdGjhuqV5IG3hu5l0IGhp4buHdSBxdeG6oyDGsHUgdGjhur8gaMahbiBj4bunYSBwaMOhYyDEkeG7kyBQUE9TIHNvIHbhu5tpIEduUkhhIMSR4buRaSB24bubaSBraOG6oyBuxINuZyDEkeG6oXQgxJHGsOG7o2MgdGhhaSBkaeG7hW4gdGnhur9uLiBT4buxIGNow6puaCBs4buHY2ggduG7gSBPZGRzIHbDoCB4w6FjIHN14bqldCB0aMOgbmggY8O0bmcgZ2nhu69hIDIgcGjDoWMgxJHhu5MgbMOgIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouDQoNClRhIGPFqW5nIGPDsyB0aOG7gyB0w61uaCBSRCwgT1IgdsOgIFJSIGNobyBjw6FjIGJp4bq/biDEkeG7i25oIGzGsOG7o25nLCBsw7pjIG7DoHkgMiBi4bqtYyBzbyBzw6FuaCBsw6AgJFgtMSQgdsOgICRYKzEkLCBr4bq/dCBxdeG6oyBjaG8gYmnhur90IGhp4buHdSDhu6luZyBraGkgWCBnaWEgdMSDbmcgMSDEkcahbiB24buLLiBC4bqjbmcgc2F1IMSRw6J5IHRyw6xuaCBiw6B5IGdpw6EgdHLhu4sgUkQsIE9SIHbDoCBSUiBjaG8gQWdlLCBBRkMsIFRoaWNrbmVzcyB2w6AgY2hvIHJpw6puZyBt4buXaSBuaMOzbSBwaMOhYyDEkeG7ky4NCg0KYGBge3J9DQpyZCA9IGF2Z19jb21wYXJpc29ucygNCiAgbG9nX21vZCwNCiAgdmFyaWFibGVzID0gbGlzdChBZ2UgPSAxLA0KICAgICAgICAgICAgICAgICAgIEFGQyA9IDEsIA0KICAgICAgICAgICAgICAgICAgIFRoaWNrbmVzcyA9IDEpLA0KICBieSA9ICJQcm90b2NvbCIpJT4lDQogIGRwbHlyOjpzZWxlY3QoLWMoMSw3LDExOjEzKSklPiUNCiAgbXV0YXRlKGNvbnRyYXN0ID0gIlJEIikNCg0KcmQgJT4lIGtuaXRyOjprYWJsZSgpDQoNCm9yID0gYXZnX2NvbXBhcmlzb25zKA0KICBsb2dfbW9kLA0KICB2YXJpYWJsZXMgPSBsaXN0KEFnZSA9IDEsDQogICAgICAgICAgICAgICAgICAgQUZDID0gMSwgDQogICAgICAgICAgICAgICAgICAgVGhpY2tuZXNzID0gMSksDQogIGJ5ID0gIlByb3RvY29sIiwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbm9yYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSAiZXhwIiklPiUNCiAgZHBseXI6OnNlbGVjdCgtYygxKSklPiUNCiAgbXV0YXRlKGNvbnRyYXN0ID0gIk9SIikNCg0KcnIgPSBhdmdfY29tcGFyaXNvbnMoDQogIGxvZ19tb2QsDQogIHZhcmlhYmxlcyA9IGxpc3QoQWdlID0gMSwNCiAgICAgICAgICAgICAgICAgICBBRkMgPSAxLCANCiAgICAgICAgICAgICAgICAgICBUaGlja25lc3MgPSAxKSwNCiAgYnkgPSAiUHJvdG9jb2wiLA0KICB0cmFuc2Zvcm1fcHJlID0gImxucmF0aW9hdmciLA0KICB0cmFuc2Zvcm1fcG9zdCA9IGV4cCklPiUNCiAgZHBseXI6OnNlbGVjdCgtYygxLDk6MTEpKSU+JQ0KICAgbXV0YXRlKGNvbnRyYXN0ID0gIlJSIikNCg0KcmJpbmQob3IscnIpICU+JSBrbml0cjo6a2FibGUoKQ0KYGBgDQpUaGVvIGvhur90IHF14bqjIG7DoHksIHRhIHRo4bqleSBBRkMgY8OzIHTGsMahbmcgcXVhbiB0aHXhuq1uIChPUiA+IDEgdsOgIFJSID4gMSkgduG7m2kgeMOhYyBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uLCBuaMawbmcgY2jhu4kgY8OzIMO9IG5naMSpYSB0cm9uZyBuaMOzbSBwaMOhYyDEkeG7kyBHblJIYTsgQWdlIGPDsyB0xrDGoW5nIHF1YW4gbmdo4buLY2ggw70gbmdoxKlhIHbhu5tpIGto4bqjIG7Eg25nIMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gY2hvIGPhuqMgMiBwaMOhYyDEkeG7kyAoT1IgPCAxIHbDoCBSUiA8IDEpLCB0cm9uZyBraGkga2jDtG5nIGPDsyBsacOqbiBo4buHIMO9IG5naMSpYSBuw6BvIGdp4buvYSBUaGlja25lc3MgdsOgIGto4bqjIG7Eg25nIMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24uDQoNClRo4buxYyByYSwgbmjhu69uZyB0cuG7iyBz4buRIG3DoCB0YSB24burYSDGsOG7m2MgbMaw4bujbmcgYmFvIGfhu5NtIFJELCBPUiBob+G6t2MgUlIgY2jhu4kgY8OzIMO9IG5naMSpYSB0w7NtIHThuq90IChhYnN0cmFjdGlvbikgdsOgIGNobyBiaeG6v3QgdGjDtG5nIHRpbiB24buBIGhp4buHdSDhu6luZyDhu58gbeG7qWMgxJHhu5kgcXXhuqduIHRo4buDLiBIaeG7h24gbmF5LCBjw7MgbeG7mXQgc+G7kSB0w6FjIGdp4bqjIGtodXnhur9uIGPDoW8gduG7gSBuaMaw4bujYyDEkWnhu4NtIGPhu6dhIHZp4buHYyBjaOG7iSBiw6FvIGPDoW8gaGnhu4d1IOG7qW5nIGNobyBiaeG6v24gbmjhu4sgcGjDom4gYuG6sW5nIG3hu5l0IHRy4buLIHPhu5EgZHV5IG5o4bqldCwgdsOgIGzhu6NpIMOtY2ggY+G7p2Egdmnhu4djIGN1bmcgY+G6pXAgdGjDtG5nIHRpbiBjaGkgdGnhur90IGjGoW4gduG7gSB0b8OgbiB0aOG7gyDEkeG6t2MgdMOtbmggcGjDom4gcGjhu5FpIGPhu6dhIGhp4buHdSDhu6luZyB0cm9uZyBt4bqrdSBraOG6o28gc8OhdC4NCg0KQmnhu4N1IMSR4buTIHNhdSDEkcOieSBjaG8gcGjDqXAgxJHhu5FpIGNoaeG6v3UgcGjDom4gcGjhu5FpIHRvw6BuIHRo4buDIGPhu6dhIHjDoWMgc3XhuqV0IMSR4bqhdCB0aGFpIGRp4buFbiB0aeG6v24gY+G7mW5nIGThu5NuIHRyb25nIDEwNyBjw6EgdGjhu4MgZMO5bmcgcGjDoWMgxJHhu5MgR25SSGEgdsOgIDk5IGPDoSB0aOG7gyBkw7luZyBwaMOhYyDEkeG7kyBQUE9TLg0KDQpgYGB7cn0NCmVmZnMgPSBjb21wYXJpc29ucyhsb2dfbW9kLA0KICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICJQcm90b2NvbCIsIA0KICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRhZ3JpZChncmlkX3R5cGUgPSAnY291bnRlcmZhY3R1YWwnKSkNCg0KZWZmcyAlPiUgZ2dwbG90KCkgKw0KICBnZW9tX2RlbnNpdHkoYWVzKHggPSBwcmVkaWN0ZWQsIGZpbGwgPSBQcm90b2NvbCksIGFscGhhID0gMC41KSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4oZWZmcyRwcmVkaWN0ZWRfbG8pLCBjb2xvciA9ICJibHVlIiwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLCBzaXplID0gMSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGVmZnMkcHJlZGljdGVkX2hpKSwgY29sb3IgPSAicmVkIiwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLCBzaXplID0gMSkgKw0KICBsYWJzKHggPSAiUHJvYmFiaWxpdHkgb2YgT1AiLCANCiAgICAgICB0aXRsZSA9ICJVbml0IGxldmVsIGNvbnRyYXN0IiwNCiAgICAgICBmaWxsID0gIlByb3RvY29sIikrDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSksIGJyZWFrcyA9IHNlcSgwLDEsMC4xKSkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHMpKw0KICB0aGVtZV9idygxMCkNCmBgYA0KQ2jDuiB0aMOtY2g6IEjDrG5oIHRyw6puIHRyw6xuaCBiw6B5IDIgYmnhu4N1IMSR4buTIG3huq10IMSR4buZIHBow6JuIHBo4buRaSBjaG8gcGjDqXAgc28gc8OhbmggxJHhurdjIMSRaeG7hW0gcGjDom4gcGjhu5FpIGPhu6dhIHjDoWMgc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biAoVHLhu6VjIFgpIGdp4buvYSAyIHBow6FjIMSR4buTOiBHblJIYSAobcOgdSB4YW5oKSB2w6AgUFBPUyAobcOgdSBo4buTbmcpLiAyIMSRxrDhu51uZyB0aOG6s25nIGtow7RuZyBsacOqbiB04bulYyB0xrDGoW5nIOG7qW5nIHbhu5tpIGdpw6EgdHLhu4sgdHJ1bmcgduG7iyBj4bunYSB4w6FjIHN14bqldCBuw6B5IOG7nyBt4buXaSBuaMOzbS4NCg0KSMOsbmgg4bqjbmggY2hvIHRo4bqleSBjw7Mgc+G7sSB0xrDGoW5nIHBo4bqjbiByw7UgbsOpdCBnaeG7r2EgMiBwaMOhYyDEkeG7kywgduG7m2kgxrB1IHRo4bq/IG5naGnDqm5nIHbhu4EgcGjDoWMgxJHhu5MgUFBPUy4NCg0KTeG7mXQgYmnhu4N1IMSR4buTIGtow6FjIGNobyBwaMOpcCBow6xuaCBkdW5nIHbhu4EgY8OhbiBjw6JuIGNow6puaCBs4buHY2ggY+G7p2EgaGnhu4d1IHF14bqjIGdp4buvYSAyIHBow6FjIMSR4buTLCDEkeG6v24gY+G6pXAgxJHhu5kgdOG7q25nIGPDoSB0aOG7gy4NCg0KYGBge3J9DQplZmZzICU+JSBnZ3Bsb3QoYWVzKHg9cHJlZGljdGVkX2xvLCB5PXByZWRpY3RlZF9oaSkpKw0KICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZWRpY3RlZF9sbywgeT1wcmVkaWN0ZWRfaGksDQogICAgICAgICAgICAgICAgIGNvbCA9IHByZWRpY3RlZF9oaSkpKw0KICBjb29yZF9maXhlZCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxKSkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoJ09QIHByb2InKSsNCiAgbGFicyh4ID0gIlByb2JhYmlsaXR5IG9mIE9QIGJ5IEduUkhfYW50IiwgeSA9ICJQcm9iYWJpbGl0eSBvZiBPUCBieSBQUE9TIikrDQogIHRoZW1lX2J3KCkNCg0KYGBgDQpDaMO6IHRow61jaDogxJDDonkgbMOgIG3hu5l0IGJp4buDdSDEkeG7kyB0w6FuIHjhuqEgY2hvIHBow6lwIGtp4buDbSB0cmEgbeG7qWMgxJHhu5kgdMawxqFuZyBwaOG6o24gLyBjaMOqbmggbOG7h2NoIGdp4buvYSAyIHBow6FjIMSR4buTIHbhu4EgeMOhYyBzdeG6pXQgdGhhaSBkaeG7hW4gdGnhur9uLiBUcuG7pWMgWCB2w6AgWSB0csOsbmggYsOgeSAyIHRoYW5nIMSRbyB4w6FjIHN14bqldCB04burIDAtMSBjaG8gbeG7l2kgcGjDoWMgxJHhu5MuIE3hu5dpIGNo4bqlbSB0csOybiBiaeG7g3UgdGjhu4sgY2hvIG3hu5l0IMSRxqFuIHbhu4sga2jhuqNvIHPDoXQgKGPDoSB0aOG7gyBi4buHbmggbmjDom4pLCDEkcaw4budbmcgcGjDom4gZ2nDoWMgY+G7p2EgYmnhu4N1IMSR4buTIGJp4buDdSB0aOG7iyBjaG8gZ2nhuqMgdGh1eeG6v3QgbMOgIGhhaSBwaMOhYyDEkeG7kyB0xrDGoW5nIMSRxrDGoW5nIGhvw6BuIHRvw6BuIHbhu5tpIG5oYXUuIE5o4buvbmcgxJFp4buDbSB0csOybiBu4bqxbSBiw6puIGTGsOG7m2kgxJHGsOG7nW5nIHBow6JuIGdpw6FjIG7DoHkgY8OzIGhp4buHdSBxdeG6oyBHblJIX2FudCDGsHUgdGjhur8gaMahbiwgdHLDoWkgbOG6oWkgduG7m2kgbmjhu69uZyDEkWnhu4NtIG7hurFtIGLDqm4gdHLDqm4gxJHGsOG7nW5nIG7DoHksIHBow6FjIMSR4buTIFBQT1MgY8OzIGhp4buHdSDhu6luZyDGsHUgdGjhur8gaMahbi4gTmjhu69uZyDEkWnhu4NtIG7hurFtIHLhuqV0IGfhuqduIHbDoCBk4buNYyB0aGVvIMSRxrDhu51uZyBuw6B5IGPDsyBoaeG7h3UgcXXhuqMgdMawxqFuZyDEkcawxqFuZyBnaeG7r2EgMiBwaMOhYyDEkeG7ky4NCg0KQ8OhYyBiaeG7g3UgxJHhu5MgdGnhur9wIHRoZW8gdHLDrG5oIGLDoHkgduG7gSBt4buRaSBsacOqbiBo4buHIGdp4buvYSBUdeG7lWksIEFGQyB2w6AgVGhpY2tuZXNzIHbhu5tpIHjDoWMgc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biwgcmnDqm5nIGNobyBt4buXaSBuaMOzbSBwaMOhYyDEkeG7kzoNCg0KYGBge3J9DQpwcmVkcyA9IHByZWRpY3Rpb25zKG1vZGVsID0gbG9nX21vZCwNCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFncmlkKEFnZSA9IHNlcSgyMCw0MCw1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByb3RvY29sID0gYygiR25SSGEiLCJQUE9TIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkX3R5cGUgPSAiY291bnRlcmZhY3R1YWwiKSkNCg0KcHJlZHMlPiVnZ3Bsb3QoYWVzKHggPSBBZ2UsDQogICAgICAgICAgICAgICAgICAgeSA9IGVzdGltYXRlKSkgKw0KICBzdGF0X2hhbGZleWUoYWxwaGEgPSAwLjUsIA0KICAgICAgICAgICAgICAgLndpZHRoID0gYygwLjc1LCAwLjk1KSwNCiAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBzdGF0X2xpbmVyaWJib24oYWVzKHkgPSBlc3RpbWF0ZSwgZmlsbCA9IFByb3RvY29sKSwgDQogICAgICAgICAgICAgICAgICAud2lkdGggPSBjKC45NSwgLjc1LCAuNTApLCANCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMS81LA0KICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArDQogIGxhYnMoeT0iT1AgcHJvYmFiaWxpdHkiLCB4ID0gIkFnZSIpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDIwLDQ1KSwgYnJlYWtzID0gc2VxKDIwLDQwLDUpKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxKSkrDQogIGZhY2V0X3dyYXAoflByb3RvY29sLCBuY29sID0gMikrDQogIHRoZW1lX2J3KDEwKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdD0xKSkgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSkNCg0KYGBgDQoNCmBgYHtyfQ0KcHJlZHMgPSBwcmVkaWN0aW9ucyhtb2RlbCA9IGxvZ19tb2QsDQogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRhZ3JpZChBRkMgPSBzZXEoMSwzMCw1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByb3RvY29sID0gYygiR25SSGEiLCJQUE9TIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkX3R5cGUgPSAiY291bnRlcmZhY3R1YWwiKSkNCg0KcHJlZHMlPiVnZ3Bsb3QoYWVzKHggPSBBRkMsDQogICAgICAgICAgICAgICAgICAgeSA9IGVzdGltYXRlKSkgKw0KICBzdGF0X2hhbGZleWUoYWxwaGEgPSAwLjUsIA0KICAgICAgICAgICAgICAgLndpZHRoID0gYygwLjc1LCAwLjk1KSwNCiAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBzdGF0X2xpbmVyaWJib24oYWVzKHkgPSBlc3RpbWF0ZSwgZmlsbCA9IFByb3RvY29sKSwgDQogICAgICAgICAgICAgICAgICAud2lkdGggPSBjKC45NSwgLjc1LCAuNTApLCANCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMS81LA0KICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArDQogIGxhYnMoeT0iT1AgcHJvYmFiaWxpdHkiLCB4ID0gIkFGQyIpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMzEpLCBicmVha3MgPSBzZXEoMCwzMCw1KSkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpKw0KICBmYWNldF93cmFwKH5Qcm90b2NvbCwgbmNvbCA9IDIpKw0KICB0aGVtZV9idygxMCkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIikpDQpgYGANCg0KYGBge3J9DQpwcmVkcyA9IHByZWRpY3Rpb25zKG1vZGVsID0gbG9nX21vZCwNCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFncmlkKFRoaWNrbmVzcyA9IHNlcSg4LDIwLDQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvdG9jb2wgPSBjKCJHblJIYSIsIlBQT1MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRfdHlwZSA9ICJjb3VudGVyZmFjdHVhbCIpKQ0KDQpwcmVkcyU+JWdncGxvdChhZXMoeCA9IFRoaWNrbmVzcywNCiAgICAgICAgICAgICAgICAgICB5ID0gZXN0aW1hdGUpKSArDQogIHN0YXRfaGFsZmV5ZShhbHBoYSA9IDAuNSwgDQogICAgICAgICAgICAgICAud2lkdGggPSBjKDAuNzUsIDAuOTUpLA0KICAgICAgICAgICAgICAgcG9pbnRfaW50ZXJ2YWwgPSAnbWVkaWFuX3FpJywNCiAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIHN0YXRfbGluZXJpYmJvbihhZXMoeSA9IGVzdGltYXRlLCBmaWxsID0gUHJvdG9jb2wpLCANCiAgICAgICAgICAgICAgICAgIC53aWR0aCA9IGMoLjk1LCAuNzUsIC41MCksIA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLzUsDQogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpICsNCiAgbGFicyh5PSJPUCBwcm9iYWJpbGl0eSIsIHggPSAiRW5kb21ldHJpYWwgdGhpY2tuZXNzIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoNywyMiksIGJyZWFrcyA9c2VxKDgsMjAsNCkpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEpKSsNCiAgZmFjZXRfd3JhcCh+UHJvdG9jb2wsIG5jb2wgPSAyKSsNCiAgdGhlbWVfYncoMTApKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0PTEpKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpKQ0KYGBgDQoNCiMgQsaw4bubYyAyQjogTcO0IGjDrG5oIGjhu5NpIHF1eSBCaW5vbWlhbCBjaG8gdOG7tyBs4buHIE9QL3Thu5VuZyBz4buRIHBow7RpDQoNClRyb25nIHBo4bqnbiB0aeG6v3AgdGhlbywgdGEgc+G6vSBwaMOibiB0w61jaCBsb+G6oWkga+G6v3QgcXXhuqMgdGjhu6kgaGFpOiB04bu3IGzhu4cgdGhhaSBkaeG7hW4gdGnhur9uIHRyw6puIHThu5VuZyBz4buRIHBow7RpIMSRw6MgY2h1eeG7g24gKGJp4bq/biBPUF9jdW0pLg0KDQpNw7QgaMOsbmggxJHGsOG7o2MgZOG7sW5nIGLhurFuZyB0aMawIHZp4buHbiBnYW1sc3MgduG7m2kgY8O6IHBow6FwIGdp4buRbmcgbmjGsCBtw7QgaMOsbmggR0xNIEJpbm9taWFsIHRyb25nIGNoxrDGoW5nIHRyxrDhu5tjLiBDw7RuZyB0aOG7qWMgY+G7p2EgbcO0IGjDrG5oIG5oxrAgc2F1OiAiY2JpbmQoT1BfY3VtLCBuX0VULU9QX2N1bSkgfiBQcm90b2NvbCAqIEFGQyArIFRoaWNrbmVzcyArIEFnZSIgKE5o4bqvYyBs4bqhaTogbcO0IGjDrG5oIEdMTSBCaW5vbWlhbCB04buVbmcgcXXDoXQgY+G6p24gMiBiaeG6v24g4bufIHbhu4sgdHLDrSBr4bq/dCBxdeG6ozogc+G7kSBr4bq/dCBxdeG6oyB0aMOgbmggY8O0bmcgbMOgIGJp4bq/biBPUF9jdW0sIHbDoCBz4buRIGvhur90IHF14bqjIHRo4bqldCBi4bqhaSwgbMOgIGhp4buHdSBz4buRIGdp4buvYSBz4buRIHBow7RpIGNodXnhu4NuIHbDoCBz4buRIHRoYWkpDQoNCnTDuXkgY2jhu4luaCBmYW1pbHkgPSBCSShtdS5saW5rID0gImxvZ2l0IikgdMawxqFuZyDhu6luZyB24bubaSBwaMOibiBwaOG7kWkgQmlub21pYWwgduG7m2kgaMOgbSBsacOqbiBr4bq/dCBsb2dpdCBjaG8gdGhhbSBz4buRICRcbXUkDQoNCk7hu5lpIGR1bmcgY+G7p2EgbcO0IGjDrG5oIG7DoHkgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KYmlfbW9kID0gZ2FtbHNzKGNiaW5kKE9QX2N1bSwgbl9FVC1PUF9jdW0pIH4gDQogICAgICAgICAgICAgICAgICBQcm90b2NvbCAqIEFGQyArIFRoaWNrbmVzcyArIEFnZSwNCiAgICAgICAgICAgICAgICBkYXRhID0gZGYsDQogICAgICAgICAgICAgICAgZmFtaWx5ID0gQkkobXUubGluayA9ICJsb2dpdCIpKQ0KDQpzdW1tYXJ5KGJpX21vZCkNCmBgYA0KDQpDw6FjaCB0aOG7qWMgZGnhu4VuIGdp4bqjaSBtw7QgaMOsbmggbsOgeSBob8OgbiB0b8OgbiBnaeG7kW5nIG5oxrAgbcO0IGjDrG5oIGxvZ2lzdGljIOG7nyB0csOqbiBuw6puIHRhIGtow7RuZyBj4bqnbiBuaOG6r2MgbOG6oWkg4bufIMSRw6J5LiBUYSBz4bq9IMSRaSB0aOG6s25nIMSR4bq/biBjw7RuZyDEkW/huqFuIMaw4bubYyB0w61uaCAzIHRy4buLIHPhu5EgbWFyZ2luYWwgZWZmZWN0cyBjaG8gcGjDoWMgxJHhu5MgUFBPUyB04burIG3DtCBow6xuaCBuw6B5Og0KDQpgYGB7cn0NCmRyID0gYXZnX2NvbXBhcmlzb25zKA0KICBiaV9tb2QsDQogIHdoYXQgPSAibXUiLA0KICB2YXJpYWJsZXMgPSAiUHJvdG9jb2wiKSU+JQ0KICBkcGx5cjo6c2VsZWN0KC1jKDEsMiw1LDYpKSU+JQ0KICBtdXRhdGUoY29udHJhc3QgPSAiRFIiKQ0KDQpvciA9IGF2Z19jb21wYXJpc29ucygNCiAgYmlfbW9kLA0KICB3aGF0ID0gIm11IiwNCiAgdmFyaWFibGVzID0gIlByb3RvY29sIiwNCiAgdHJhbnNmb3JtX3ByZSA9ICJsbm9yYXZnIiwNCiAgdHJhbnNmb3JtX3Bvc3QgPSAiZXhwIiklPiUNCiAgZHBseXI6OnNlbGVjdCgtYygxLDIsODoxMCkpJT4lDQogIG11dGF0ZShjb250cmFzdCA9ICJPUiIpDQoNCnJyID0gYXZnX2NvbXBhcmlzb25zKA0KICBiaV9tb2QsDQogIHdoYXQgPSAibXUiLA0KICB2YXJpYWJsZXMgPSAiUHJvdG9jb2wiLA0KICB0cmFuc2Zvcm1fcHJlID0gImxucmF0aW9hdmciLA0KICB0cmFuc2Zvcm1fcG9zdCA9IGV4cCklPiVkcGx5cjo6c2VsZWN0KC1jKDEsMiw4OjEwKSklPiUNCiAgIG11dGF0ZShjb250cmFzdCA9ICJSUiIpDQoNCnJiaW5kKGRyLCBvcixycikgJT4lIGtuaXRyOjprYWJsZSgpDQpgYGANClRoZW8ga+G6v3QgcXXhuqMgbsOgeSwgY8OzIGxpw6puIGjhu4cgw70gbmdoxKlhIGdp4buvYSBwaMOhYyDEkeG7kyBQUE9TIHbDoCBz4buxIGdpYSB0xINuZyBj4bunYSB04bu3IGzhu4cgdGhhaSBkaeG7hW4gdGnhur9uIGPhu5luZyBk4buTbi4gQ+G7pSB0aOG7gyBuaMOzbSBwaMOhYyDEkeG7kyBQUE9TIGPDsyB04bu3IGzhu4cgdGhhaSBkaeG7hW4gdGnhur9uIGNhbyBoxqFuIHRydW5nIGLDrG5oIDguMzclIChEUiA9IDAuMDgzNywgS1RDOTUlOiAwLjAwMiAtIDAuMTY1LCBwPTAuMDQpLiBU4bu3IHPhu5EgT2RkcyAoT1IpIHbDoCB04bu3IHPhu5Egbmd1eSBjxqEgKFJSKSBs4bqnbiBsxrDhu6N0IGzDoCAxLjQ0IHbDoCAxLjI3IMSR4buBdSBjYW8gaMahbiAxIGNobyB0aOG6pXkgaGnhu4d1IHF14bqjIMawdSB0aOG6vyBoxqFuIGPhu6dhIHBow6FjIMSR4buTIFBQT1MsIGPhuqMgaGFpIMSR4buBdSBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqLg0KDQpUYSB0aOG7sWMgaGnhu4duIG3DtCB04bqjIHBow6JuIHBo4buRaSB0b8OgbiB0aOG7gyBj4bunYSBoaeG7h3Ug4bupbmcgbsOgeSBxdWEgaGFpIGJp4buDdSDEkeG7kyBzYXU6DQoNCmBgYHtyfQ0KZWZmcyA9IGNvbXBhcmlzb25zKGJpX21vZCwNCiAgICAgICAgICAgICAgICAgICB3aGF0ID0gIm11IiwNCiAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiUHJvdG9jb2wiLCANCiAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZGF0YWdyaWQoZ3JpZF90eXBlID0gJ2NvdW50ZXJmYWN0dWFsJykpDQoNCmVmZnMgJT4lIGdncGxvdCgpICsNCiAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gcHJlZGljdGVkLCBmaWxsID0gUHJvdG9jb2wpLCBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGVmZnMkcHJlZGljdGVkX2xvKSwgY29sb3IgPSAiYmx1ZSIsIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwgc2l6ZSA9IDEpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihlZmZzJHByZWRpY3RlZF9oaSksIGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwgc2l6ZSA9IDEpICsNCiAgbGFicyh4ID0gIlByb2JhYmlsaXR5IG9mIE9QIiwgDQogICAgICAgdGl0bGUgPSAiVW5pdCBsZXZlbCBjb250cmFzdCIsDQogICAgICAgZmlsbCA9ICJwcm90b2NvbCIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEpLCBicmVha3MgPSBzZXEoMCwxLDAuMSkpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxzKSsNCiAgdGhlbWVfYncoMTApDQpgYGANCkjDrG5oIOG6o25oIG7DoHkgY2hvIHRo4bqleSBt4bq3YyBkw7kgcGjhuqduIHRyw7luZyBs4bq3cCBnaeG7r2EgcGjDom4gcGjhu5FpIGPhu6dhIHjDoWMgc3XhuqV0IHRoYWkgZGnhu4VuIHRp4bq/biDhu58gMiBwaMOhYyDEkeG7kyBsw6Aga2jDoSBjYW8sIHbhuqtuIGPDsyBt4buZdCBi4buZIHBo4bqtbiBjw6EgdGjhu4MgY2hvIHRo4bqleSBoaeG7h3UgcXXhuqMgxrB1IHRo4bq/IGjGoW4gY+G7p2EgcGjDoWMgxJHhu5MgUFBPUywgdsOgIGdpw6EgdHLhu4sgdHJ1bmcgduG7iyBj4bunYSAyIHBow6JuIHBo4buRaSBjw6FjaCBuaGF1IG3hu5l0IGtob+G6o25nIGfhuqduIGLhurFuZyAwLjEuDQoNClTGsMahbmcgdOG7sSwgdHLDqm4gYmnhu4N1IMSR4buTIHTGsMahbmcgaOG7o3AgduG7gSB4w6FjIHN14bqldCB0aGFpIGRp4buFbiB0aeG6v24g4bufIDIgcGjDoWMgxJHhu5MsIHBo4bqnbiBs4bubbiBjw6EgdGjhu4MgbuG6sW0g4bufIHbhu4sgdHLDrSBwaMOtYSB0csOqbiDEkcaw4budbmcgcGjDom4gZ2nDoWMsIGNobyB0aOG6pXkgc+G7sSBjaMOqbmggbOG7h2NoIHbhu4EgxrB1IHRo4bq/IG5naGnDqm5nIHbhu4EgcGjDrWEgcGjDoWMgxJHhu5MgUFBPUy4NCg0KYGBge3J9DQplZmZzICU+JSBnZ3Bsb3QoYWVzKHg9cHJlZGljdGVkX2xvLCB5PXByZWRpY3RlZF9oaSkpKw0KICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZWRpY3RlZF9sbywgeT1wcmVkaWN0ZWRfaGksDQogICAgICAgICAgICAgICAgIGNvbCA9IHByZWRpY3RlZF9oaSkpKw0KICBjb29yZF9maXhlZCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxKSkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoJ09QIHByb2InKSsNCiAgbGFicyh4ID0gIlByb2JhYmlsaXR5IG9mIE9QIGJ5IEduUkhfYW50IiwgeSA9ICJQcm9iYWJpbGl0eSBvZiBPUCBieSBQUE9TIikrDQogIHRoZW1lX2J3KCkNCmBgYA0KQ3Xhu5FpIGPDuW5nLCB0YSBjxaluZyBraOG6o28gc8OhdCB24buBIGxpw6puIGjhu4cgZ2nhu69hIFR14buVaSwgQUZDIHbDoCBUaGlja25lc3MgduG7m2kgdOG7tyBs4buHIHRoYWkgZGnhu4VuIHRp4bq/biBj4buZbmcgZOG7k24gdMO5eSB0aGVvIHBow6FjIMSR4buTLg0KDQpgYGB7cn0NCnByZWRzID0gcHJlZGljdGlvbnMobW9kZWwgPSBiaV9tb2QsDQogICAgICAgICAgICAgICAgICAgIHdoYXQgPSAibXUiLA0KICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZGF0YWdyaWQoQWdlID0gc2VxKDIwLDQwLDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvdG9jb2wgPSBjKCJHblJIYSIsIlBQT1MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRfdHlwZSA9ICJjb3VudGVyZmFjdHVhbCIpKQ0KDQpwcmVkcyU+JWdncGxvdChhZXMoeCA9IEFnZSwNCiAgICAgICAgICAgICAgICAgICB5ID0gZXN0aW1hdGUpKSArDQogIHN0YXRfaGFsZmV5ZShhbHBoYSA9IDAuNSwgDQogICAgICAgICAgICAgICAgICAgIC53aWR0aCA9IGMoMC43NSwgMC45NSksDQogICAgICAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIHN0YXRfbGluZXJpYmJvbihhZXMoeSA9IGVzdGltYXRlLCBmaWxsID0gUHJvdG9jb2wpLCANCiAgICAgICAgICAgICAgICAgIC53aWR0aCA9IGMoLjk1LCAuNzUsIC41MCksIA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLzUsDQogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpICsNCiAgbGFicyh5PSJPUCBwcm9iYWJpbGl0eSIsIHggPSAiQWdlIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMjAsNDUpLCBicmVha3MgPSBzZXEoMjAsNDAsNSkpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEpKSsNCiAgZmFjZXRfd3JhcCh+UHJvdG9jb2wsIG5jb2wgPSAyKSsNCiAgdGhlbWVfYncoMTApKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0PTEpKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpKQ0KDQpgYGANCg0KYGBge3J9DQpwcmVkcyA9IHByZWRpY3Rpb25zKG1vZGVsID0gYmlfbW9kLA0KICAgICAgICAgICAgICAgICAgICB3aGF0ID0gIm11IiwNCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFncmlkKEFGQyA9IHNlcSgxLDMwLDUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvdG9jb2wgPSBjKCJHblJIYSIsIlBQT1MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRfdHlwZSA9ICJjb3VudGVyZmFjdHVhbCIpKQ0KDQpwcmVkcyU+JWdncGxvdChhZXMoeCA9IEFGQywNCiAgICAgICAgICAgICAgICAgICB5ID0gZXN0aW1hdGUpKSArDQogIHN0YXRfaGFsZmV5ZShhbHBoYSA9IDAuNSwgDQogICAgICAgICAgICAgICAud2lkdGggPSBjKDAuNzUsIDAuOTUpLA0KICAgICAgICAgICAgICAgcG9pbnRfaW50ZXJ2YWwgPSAnbWVkaWFuX3FpJywNCiAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIHN0YXRfbGluZXJpYmJvbihhZXMoeSA9IGVzdGltYXRlLCBmaWxsID0gUHJvdG9jb2wpLCANCiAgICAgICAgICAgICAgICAgIC53aWR0aCA9IGMoLjk1LCAuNzUsIC41MCksIA0KICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLzUsDQogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpICsNCiAgbGFicyh5PSJPUCBwcm9iYWJpbGl0eSIsIHggPSAiQUZDIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwzMSksIGJyZWFrcyA9IHNlcSgwLDMwLDUpKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxKSkrDQogIGZhY2V0X3dyYXAoflByb3RvY29sLCBuY29sID0gMikrDQogIHRoZW1lX2J3KDEwKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdD0xKSkgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSkNCg0KYGBgDQoNCmBgYHtyfQ0KcHJlZHMgPSBwcmVkaWN0aW9ucyhtb2RlbCA9IGJpX21vZCwNCiAgICAgICAgICAgICAgICAgICAgd2hhdCA9ICJtdSIsDQogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRhZ3JpZChUaGlja25lc3MgPSBzZXEoOCwyMCw0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByb3RvY29sID0gYygiR25SSGEiLCJQUE9TIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkX3R5cGUgPSAiY291bnRlcmZhY3R1YWwiKSkNCg0KcHJlZHMlPiVnZ3Bsb3QoYWVzKHggPSBUaGlja25lc3MsDQogICAgICAgICAgICAgICAgICAgeSA9IGVzdGltYXRlKSkgKw0KICBzdGF0X2hhbGZleWUoYWxwaGEgPSAwLjUsIA0KICAgICAgICAgICAgICAgLndpZHRoID0gYygwLjc1LCAwLjk1KSwNCiAgICAgICAgICAgICAgIHBvaW50X2ludGVydmFsID0gJ21lZGlhbl9xaScsDQogICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICBzdGF0X2xpbmVyaWJib24oYWVzKHkgPSBlc3RpbWF0ZSwgZmlsbCA9IFByb3RvY29sKSwgDQogICAgICAgICAgICAgICAgICAud2lkdGggPSBjKC45NSwgLjc1LCAuNTApLCANCiAgICAgICAgICAgICAgICAgIGFscGhhID0gMS81LA0KICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArDQogIGxhYnMoeT0iT1AgcHJvYmFiaWxpdHlzIiwgeCA9ICJFbmRvbWV0cmlhbCB0aGlja25lc3MiKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYyg3LDIyKSwgYnJlYWtzID1zZXEoOCwyMCw0KSkrDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpKw0KICBmYWNldF93cmFwKH5Qcm90b2NvbCwgbmNvbCA9IDIpKw0KICB0aGVtZV9idygxMCkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIikpDQoNCmBgYA0KDQojIERp4buFbiBnaeG6o2kga+G6v3QgcXXhuqMgY+G7p2EgcGjDom4gdMOtY2gNCg0KUGjDom4gdMOtY2ggZOG7ryBsaeG7h3UgbsOgeSBjaG8gcGjDqXAgcsO6dCByYSBt4buZdCBz4buRIGvhur90IGx14bqtbiBuaMawIHNhdTogDQoNCisgS2jhuqMgbsSDbmcgxJHhuqF0IMSRxrDhu6NjIHRoYWkgZGnhu4VuIHRp4bq/biBjw7MgdGjhu4MgxJHGsOG7o2Mga2jhuqNvIHPDoXQgZMaw4bubaSBuaGnhu4F1IGjDrG5oIHRo4bupYzogeMOhYyBzdeG6pXQgcXVhbiBzw6F0IMSRxrDhu6NjIG3hu5l0IGvhur90IHF14bqjIHRow6BuaCBjw7RuZyAoYmnhur9uIG5o4buLIGdpw6EgMCwxKSB0csOqbiBjw6EgdGjhu4MgYuG7h25oIG5ow6JuLCB4w6FjIHN14bqldCB0aMOgbmggY8O0bmcgY2hvIG3hu5dpIMSRxqFuIHbhu4sgbm/Do24gLSBoYXkgdOG7tyBs4buHIHRow6BuaCBjw7RuZyB0csOqbiB04buVbmcgc+G7kSBwaMO0aSDEkcaw4bujYyBjaHV54buBbi4gQ8OhY2ggdGjhu6ljIGto4bqjbyBzw6F0IGPDsyB0aOG7gyDhuqNuaCBoxrDhu59uZyDEkeG6v24ga+G6v3QgcXXhuqMgcGjDom4gdMOtY2ggdGjhu5FuZyBrw6ogKHRyb25nIHRyxrDhu51uZyBo4bujcCBoaeG7h24gdGjhu51pLCBj4bqjIDIgY8OhY2gga2jhuqNvIHPDoXQgxJHhu4F1IGNobyByYSB0aMO0bmcgxJFp4buHcCB0xrDGoW5nIMSR4buTbmcsIHR1eSBuaGnDqm4gY8OzIHPhu7Ega2jDoWMgYmnhu4d0IHbhu4EgZ2nDoSB0cuG7iyBPUiwgUlIgaG/hurdjIFJEKS4gDQoNCisgRMO5IGto4bqjbyBzw6F0IGTGsOG7m2kgaMOsbmggdGjhu6ljIG7DoG8sIHRow6wgxJHhu4F1IGThuqtuIHbhu4EgY8O0bmcgY+G7pSBjaHVuZyBsw6AgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgQmlub21pYWwgduG7m2kgMiB0aGFtIHPhu5EgJG4kLCRcbXUkLiBNw7QgaMOsbmggbG9naXN0aWMgY+G7lSDEkWnhu4NuIGzDoCB0csaw4budbmcgaOG7o3AgxJHhurdjIGJp4buHdCBraGkgJG49MSQuIEPDoWNoIGRp4buFbiBnaeG6o2kgdsOgIHN1eSBsdeG6rW4gdGjhu5FuZyBrw6ogbMOgIG5oxrAgbmhhdSBjaG8gY+G6oyAyIG3DtCBow6xuaC4NCg0KKyBNw7QgaMOsbmggbG9naXN0aWMgKGhheSBHTE0gQmlub21pYWwpIGNobyBwaMOpcCBzdXkgbHXhuq1uIHRo4buRbmcga8OqIHbhu4EgbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2EgMSBiaeG6v24gxJHhu5ljIGzhuq1wIHbDoCBt4buZdCBr4bq/dCBxdeG6oyB4w6FjIHN14bqldC4gVGEgY8OzIHRo4buDIMOhcCBk4bulbmcgbcO0IGjDrG5oIG7DoHkgY2hvIG5o4buvbmcga+G6v3QgY+G7pWMgbMOibSBzw6BuZyBuaOG7iyBwaMOibiwgaG/hurdjIHThu7cgbOG7hy4NCg==