1 Giới thiệu

Thuyên tắc phổi là tình trạng một hay nhiều động mạch trong phổi bị nghẽn bởi một cục máu đông di chuyển từ một phần khác của cơ thể, như chi dưới (huyết khối tĩnh mạch sâu (DVT)). Thuyên tắc phổi có thể gây tử vong nếu bệnh nhân không được chẩn đoán và điều trị kịp thời. Chụp động mạch phổi với thuốc cản quang là xét nghiệm chính xác nhất để chẩn đoán tắc mạch phổi, nhưng có tính xâm lấn và nguy cơ cao nên chỉ được thực hiện khi các xét nghiệm khác không cung cấp đủ bằng chứng.Lượng D-dimer trong máu tăng có thể gợi ý về sự hiện diện của cục máu đông, tuy nhiên D-dimer cũng có thể tăng bởi các yếu tố khác, như phẫu thuật, mang thai.

Đầu tiên, chúng ta trích xuất bộ dữ liệu PE từ package HydeNet, cần ghi chú đây chỉ là một dữ liệu mô phỏng với mục tiêu minh họa chứ không dựa trên nghiên cứu có thực.

library(tidyverse)
library(HydeNet)

data(PE)

PE%>%head()
str(PE)
## 'data.frame':    10000 obs. of  7 variables:
##  $ wells   : num  3 2 3 3 2 6 4 6 4 2 ...
##  $ pregnant: Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ pe      : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 2 2 1 2 1 ...
##  $ angio   : Factor w/ 2 levels "Negative","Positive": 1 2 1 1 1 2 2 1 1 1 ...
##  $ d.dimer : num  206 180 197 192 281 318 303 234 229 235 ...
##  $ treat   : Factor w/ 2 levels "No","Yes": 1 2 1 2 2 2 2 1 1 1 ...
##  $ death   : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 2 1 2 1 ...

Như ta thấy, PE là 1 dataframe với 10000 trường hợp (bệnh nhân), với 7 biến lần lượtco& nội dung:

  1. wells là 1 biến số rời rạc, trình bày điểm lâm sàng theo tiêu chí Wells.

  2. pregnant là 1 biến nhị phân, chỉ tình trạng Có/không mang thai.

  3. pe là 1 biến nhị phân, cho biết chẩn đoán của bệnh thuyến tắc phổi.

  4. angio là 1 biến nhị phân, trình bày kết quả xét nghiệm xạ hình động mạch phổi với thuốc cản quang, với 2 giá trị Positive/Negative.

  5. d.dimer là biến số liên tục, trình bày kết quả xét nghiệm định lượng D.dimer trong máu.

  6. treat là 1 biến nhị phân, là quyết định điều trị : Có/không.

  7. death là 1 biến nhị phân, cho biết kết cục của bệnh nhân: No=sống sót, Yes=Tử vong

2 Bước 1: Thiết kế cấu trúc mạng mô hình

Với dữ liệu hiện thời, rất nhiều hướng nghiên cứu có thể đặt ra, với các giả thuyết/câu hỏi khác nhau. Thí dụ: Ta có thể dựng một mô hình nhằm tiên lượng nguy cơ thuyên tắc phổi (kết quả = biến pe) dựa vào các thông tin lâm sàng (điểm wells, kết quả xét nghiệm d.dimer, angiography); ta cũng có thể nhắm đến kết cục “tử vong” của bệnh nhân và khảo sát mối liên hệ giữa kết cục này và chẩn đoán (pe) và chỉ định điều trị (treat);Cũng có thể ta muốn khảo sát sự ảnh hưởng của kết quả các xét nghiệm và điểm wells với quyết định điều trị của bác sĩ…

Tuy nhiên, trong bài thực hành hôm nay Nhi sẽ cùng các bạn đặt ra một cách tiếp cận vấn đề toàn diện và phức tạp hơn, với mục tiêu/ý tưởng:

Dựng mô hình cho phép khảo sát được toàn bộ (mạng lưới) những mối liên hệ / ảnh hưởng khả dĩ giữa các yếu tố: Bệnh lý (pe), kết quả xét nghiệm cận lâm sàng (d.dimer, angio), cảm nhận chủ quan (wells), quyết định điều trị (treat), và kết cục lâm sàng (nguy cơ tử vong) của bệnh nhân. Kết quả mô hình bao gồm chiều hướng/kích thước của mỗi hiệu ứng /liên hệ.

Mục tiêu này có thể hiểu như: tôi muốn dùng phân tích thống kê để suy diễn nhân/quả: giữa các yếu tố, và tôi muốn kể một câu chuyện có ý nghĩa dựa trên dữ liệu này.

Khác với những phương pháp phân tích mô hình đa biến quy ước - vốn thường quá chú trọng về thống kê, thí dụ chọn lọc mô hình / biến số dựa vào các chỉ số BIC, AIC (Stepwise) hoặc BMA, các phương pháp rút gọn mô hình như Lasso…, trong bài toán này chúng ta sẽ ưu tiên xây dựng mạng lưới tương tác giữa các yếu tố một cách chủ quan, dựa vào kiến thức y học, kinh nghiệm và quy luật hợp lý khác.

Ta có thể bắt đầu việc này như sau:

Trước hết, mạng lưới mô hình của chúng ta có 7 nút , trong đó 2 nút quan trọng nhất là Chẩn đoán (pe) và Điều trị (treat), nút tận cùng là kết cục: sống sót hay tử vong (death).

Ở thời điểm nhập viện, chưa có xét nghiệm nào được làm, bệnh nhân được khai thác bệnh sử và khám bởi bác sĩ, do đó điểm khởi đầu của mạng lưới chính là nút wells. Như ta biết, Wells là một thang điểm lâm sàng tiên lượng nguy cơ thuyên tắc phổi, dựa vào các tiêu chí như bệnh sử (bất động kéo dài, phẫu thuật, bệnh lý tĩnh mạch, điều trị ung thư), triệu chứng (dấu hiệu huyết khối tĩnh mạch, ho ra máu, nhịp tim nhanh…). Ta có thể nói chẩn đoán lâm sàng (pe) phụ thuộc thang điểm Wells, từ đó vạch ra liên kết đầu tiên trong mạng lưới: pe | wells. Ta có thể trình bày quan hệ điều kiện này bằng một mô hình logistic đơn biến.

Tiếp theo, ta xét nút D.dimer như biến kết quả. Có thể nói giá trị d.dimer phụ thuộc vào tình trạng có thuyên tắc phổi hay không, do đó có quan hệ giữa d.dimer và pe; tuy nhiên kết quả xét nghiệm d.dimer có thể bị sai lệch khi có thai (pregnant = Yes, với nguy cơ dương tính giả, do đó cả pe và pregnant cùng tham gia vào mô hình tiên lượng d.dimer.

kết quả xét nghiệm Angiography chỉ phụ thuộc vào sự hiện diện thuyên tắc phổi (pe) hay không, nên chỉ có 1 liên kết từ pe đến angio.

Tiếp theo, ta xét đến nút: chỉ định điều trị (treat): Bệnh nhân chỉ được điều trị khi có xét nghiệm d.dimer và/hoặc angio dương tính, xác định chẩn đoán; do đó ta có thể dùng một mô hình logistic khác với 2 biến là angio và d.dimer.

Cuối cùng, nút tận cùng của mạng lưới là kết cục tử vong của bệnh nhân, đây là kết quả cửa sự tương tác giữa 2 điều kiện: bệnh lý thuyên tắc phổi (pe) và chỉ định điều trị (treat). Thí dụ: ở bệnh nhân có thuyên tắc phổi , nguy cơ tử vong có thể thấp hơn nếu được điều trị kịp thời, ngược lại nguy cơ tử vong cao nếu không được điều trị.

Sau khi phác thảo mạng mô hình trên giấy, Nhi dùng package HydeNet để thiết kế cấu trúc của mạng lưới mô hình và vẽ sơ đồ :

net <- HydeNetwork(~ wells
                   + pe | wells
                   + d.dimer | pregnant*pe
                   + angio | pe
                   + treat | d.dimer*angio
                   + death | pe*treat)

HydePlotOptions(variable=list(fillcolor = "gold"))

plot(net)

Bên trong object net này có chứa sẵn một ma trận DAG (Directed Acyclic graph), Nhi muốn bật mí cho các bạn, vì từ matrix DAG này ta có thể vẽ ra sơ đồ mạng tùy thích bằng package igraph và ggraph.

net$dag
##          wells pe d.dimer pregnant angio treat death
## wells        0  1       0        0     0     0     0
## pe           0  0       1        0     1     0     1
## d.dimer      0  0       0        0     0     1     0
## pregnant     0  0       1        0     0     0     0
## angio        0  0       0        0     0     1     0
## treat        0  0       0        0     0     0     1
## death        0  0       0        0     0     0     0

3 Bước 2: Chuyển sơ đồ mạng thành mạng mô hình Bayes

Dựa vào sơ đồ mạng mô hình như trên, ta có thể hình dung về nó như một tập hợp 5 mô hình tương ứng với 5 nút kết quả: pe, angio, d.dimer, treat và death. Như vậy nếu ta xác định thành công 5 mô hình con này, xem như mục tiêu đã đạt được.

3.1 Phân tích hồi quy với hàm GLM

3.1.1 PE

Mô hình cho liên kết Wells - PE có dạng một mô hình logistic đơn biến, với giả định kết quả xác suất PE được ước tính bằng phân bố binomial, link function là hàm logit

binomial_smooth <- function(...) {
  geom_smooth(method = "glm", method.args = list(family = "binomial"), 
              col="black",fill="gold",linetype=2)}

PE%>%mutate(diag=as.numeric(pe)-1)%>%
  ggplot(aes(x=wells,y=diag))+
  geom_point(aes(col=pe))+
  binomial_smooth(formula = y ~ x,alpha=0.3)+
  theme_bw()

pe_lm<-glm(pe ~ wells, family="binomial",data=PE)

summary(pe_lm)
## 
## Call:
## glm(formula = pe ~ wells, family = "binomial", data = PE)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.9995  -0.6063  -0.4636  -0.3517   2.5935  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -3.90355    0.08308  -46.98   <2e-16 ***
## wells        0.57570    0.01754   32.81   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 9524.2  on 9999  degrees of freedom
## Residual deviance: 8238.9  on 9998  degrees of freedom
## AIC: 8242.9
## 
## Number of Fisher Scoring iterations: 5
pe_lm%>%broom::tidy()%>%
  mutate(OR=exp(estimate),
         UL=exp(estimate+1.96*std.error),
         LL=exp(estimate-1.96*std.error))%>%
  select(term,OR,LL,UL,p.value)

3.1.2 Angio

Mô hình cho liên kết pe-angio cũng là một mô hình logistic đơn biến:

angio_lm<-glm(angio ~ pe, family="binomial",data=PE)

summary(angio_lm)
## 
## Call:
## glm(formula = angio ~ pe, family = "binomial", data = PE)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.6469  -0.4528  -0.4528  -0.4528   2.1580  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -2.22585    0.03730  -59.67   <2e-16 ***
## peYes        3.28411    0.06516   50.40   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 10425.6  on 9999  degrees of freedom
## Residual deviance:  7309.1  on 9998  degrees of freedom
## AIC: 7313.1
## 
## Number of Fisher Scoring iterations: 4
angio_lm%>%broom::tidy()%>%
  mutate(OR=exp(estimate),
         UL=exp(estimate+1.96*std.error),
         LL=exp(estimate-1.96*std.error))%>%
  select(term,OR,LL,UL,p.value)

3.1.3 D.Dimer

Mô hình cho liên kết (d.dimer | pregnant*pe) là một mô hình hồi quy tuyến tính, với giả định biến kết quả d.dimer được ước lượng bằng phân bố Gaussian, phụ thuộc vào 2 biến pregnant và pe:

ddimer_lm<-glm(d.dimer ~ pregnant + pe,data=PE)

summary(ddimer_lm)
## 
## Call:
## glm(formula = d.dimer ~ pregnant + pe, data = PE)
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -103.243   -19.622    -0.243    19.757   107.757  
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 210.2425     0.3450  609.46   <2e-16 ***
## pregnantYes  29.2950     1.0021   29.23   <2e-16 ***
## peYes        68.3794     0.7723   88.54   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for gaussian family taken to be 892.5316)
## 
##     Null deviance: 16687526  on 9999  degrees of freedom
## Residual deviance:  8922638  on 9997  degrees of freedom
## AIC: 96324
## 
## Number of Fisher Scoring iterations: 2

3.1.4 Treat

Mô hình cho liên kết (treat | d.dimer*angio) có dạng logistic, 2 biến :

treat_lm<-glm(treat ~ angio + d.dimer,family="binomial",data=PE)

summary(treat_lm)
## 
## Call:
## glm(formula = treat ~ angio + d.dimer, family = "binomial", data = PE)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.3985  -0.6798  -0.5073   0.6114   2.6499  
## 
## Coefficients:
##                 Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -5.8931574  0.1652900  -35.65   <2e-16 ***
## angioPositive  1.7335361  0.0596181   29.08   <2e-16 ***
## d.dimer        0.0199375  0.0007072   28.19   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 12247.6  on 9999  degrees of freedom
## Residual deviance:  9466.8  on 9997  degrees of freedom
## AIC: 9472.8
## 
## Number of Fisher Scoring iterations: 4
treat_lm%>%broom::tidy()%>%
  mutate(OR=exp(estimate),
         UL=exp(estimate+1.96*std.error),
         LL=exp(estimate-1.96*std.error))%>%
  select(term,OR,LL,UL,p.value)

3.1.5 Death

death_lm<-glm(death ~ pe + treat,family="binomial",data=PE)
             
summary(death_lm)
## 
## Call:
## glm(formula = death ~ pe + treat, family = "binomial", data = PE)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.7525  -0.1736  -0.1736  -0.0662   3.5002  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -4.1876     0.1008  -41.53   <2e-16 ***
## peYes         5.4808     0.1406   38.99   <2e-16 ***
## treatYes     -1.9358     0.1163  -16.64   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 6382.2  on 9999  degrees of freedom
## Residual deviance: 3278.3  on 9997  degrees of freedom
## AIC: 3284.3
## 
## Number of Fisher Scoring iterations: 7
death_lm%>%broom::tidy()%>%
  mutate(OR=exp(estimate),
         UL=exp(estimate+1.96*std.error),
         LL=exp(estimate-1.96*std.error))%>%
  select(term,OR,LL,UL,p.value)

3.2 Mạng mô hình Bayes sử dụng brms và STAN

Bây giờ, Nhi sẽ dựng một mạng mô hình Bayes thực sự dùng ngôn ngữ STAN qua giao thức là package brms.

Trong các bài trước, Nhi đã từng giới thiệu cho các bạn package brms là một công cụ rất mạnh mẽ, linh hoạt cho phân tích Bayes. Một trong những tính năng mà brms hỗ trợ là dựng mô hình mạng lưới cấu trúc (bạn có thể gọi phương pháp này bằng nhiều cái tên: SEM, DAG, Latent variable analysis hay Bayesian network…, nhưng cách thực hiện cũng như nhau). Tính năng này có bản chất là ước lượng (MCMC sampling) đồng thời cho hàng loạt mô hình nằm trong một mạng lưới cấu trúc phức tạp. brms cho phép áp dụng priors, family và công thức riêng biệt cho mỗi mô hình bên trong mạng lưới, do đó hết sức linh hoạt khi đối diện với các bài toán phức tạp.

Trong bài thực hành này, Nhi đơn giản vấn đề bằng cách chỉ áp dụng 2 kiểu mô hình: logistic và Gaussian, và không tùy chỉnh sâu về priors.

Trước hết, ta viết công thức cho 5 mô hình theo cú pháp brms:

library(brms)

pe_mod<-bf(pe ~ wells, family=bernoulli(link = "logit"))
angio_mod<-bf(angio ~ pe, family=bernoulli(link = "logit"))
ddimer_mod<-bf(d.dimer ~ pregnant + pe)
treat_mod<-bf(treat ~ angio + d.dimer,family=bernoulli(link = "logit"))
death_mod<-bf(death ~ pe + treat,family=bernoulli(link = "logit"))

Sau đó, ta dùng hàm brm để đóng gói tất cả mô hình trong cùng 1 chế độ lấy mẫu MCMC: với 1 chuỗi, 1000 lượt khởi động, 2000 lượt lấy mẫu, sau đó rút gọn 50%.

brms sẽ compile mô hình thành STAN code, sau đó thi hành. Tiến trình tương đối chậm, tùy vào máy tính mạnh hay yếu (Nhi mất khoảng 15 phút), nhưng mô hình rồi cũng sẽ converge xong.

k_fit_brms <- brm(pe_mod +
                    angio_mod +
                    ddimer_mod +
                    treat_mod +
                    death_mod+
                    set_rescor(FALSE),
                  data=PE,
                  iter = 3000, 
                  warmup = 1000,
                  chains = 1, 
                  thin = 2,
                  refresh = 0,
                  control=list(adapt_delta=0.95,
                               max_treedepth=20))
## 
## Gradient evaluation took 0.01 seconds
## 1000 transitions using 10 leapfrog steps per transition would take 100 seconds.
## Adjust your expectations accordingly!
## 
## 
## 
##  Elapsed Time: 1021.12 seconds (Warm-up)
##                528.894 seconds (Sampling)
##                1550.01 seconds (Total)

4 Bước 3: Khảo sát nội dung mạng mô hình Bayes

Trước hết, Nhi trích nội dung STAN code của mô hình vừa dựng, code này chỉ nhằm thỏa mãn sự tò mò của các bạn thích học viết code thủ công bằng STAN

stancode(k_fit_brms )
## // generated with brms 2.3.1
## functions { 
## } 
## data { 
##   int<lower=1> N;  // total number of observations 
##   int Y_pe[N];  // response variable 
##   int<lower=1> K_pe;  // number of population-level effects 
##   matrix[N, K_pe] X_pe;  // population-level design matrix 
##   int Y_angio[N];  // response variable 
##   int<lower=1> K_angio;  // number of population-level effects 
##   matrix[N, K_angio] X_angio;  // population-level design matrix 
##   vector[N] Y_ddimer;  // response variable 
##   int<lower=1> K_ddimer;  // number of population-level effects 
##   matrix[N, K_ddimer] X_ddimer;  // population-level design matrix 
##   int Y_treat[N];  // response variable 
##   int<lower=1> K_treat;  // number of population-level effects 
##   matrix[N, K_treat] X_treat;  // population-level design matrix 
##   int Y_death[N];  // response variable 
##   int<lower=1> K_death;  // number of population-level effects 
##   matrix[N, K_death] X_death;  // population-level design matrix 
##   int prior_only;  // should the likelihood be ignored? 
## } 
## transformed data { 
##   int Kc_pe = K_pe - 1; 
##   matrix[N, K_pe - 1] Xc_pe;  // centered version of X_pe 
##   vector[K_pe - 1] means_X_pe;  // column means of X_pe before centering 
##   int Kc_angio = K_angio - 1; 
##   matrix[N, K_angio - 1] Xc_angio;  // centered version of X_angio 
##   vector[K_angio - 1] means_X_angio;  // column means of X_angio before centering 
##   int Kc_ddimer = K_ddimer - 1; 
##   matrix[N, K_ddimer - 1] Xc_ddimer;  // centered version of X_ddimer 
##   vector[K_ddimer - 1] means_X_ddimer;  // column means of X_ddimer before centering 
##   int Kc_treat = K_treat - 1; 
##   matrix[N, K_treat - 1] Xc_treat;  // centered version of X_treat 
##   vector[K_treat - 1] means_X_treat;  // column means of X_treat before centering 
##   int Kc_death = K_death - 1; 
##   matrix[N, K_death - 1] Xc_death;  // centered version of X_death 
##   vector[K_death - 1] means_X_death;  // column means of X_death before centering 
##   for (i in 2:K_pe) { 
##     means_X_pe[i - 1] = mean(X_pe[, i]); 
##     Xc_pe[, i - 1] = X_pe[, i] - means_X_pe[i - 1]; 
##   } 
##   for (i in 2:K_angio) { 
##     means_X_angio[i - 1] = mean(X_angio[, i]); 
##     Xc_angio[, i - 1] = X_angio[, i] - means_X_angio[i - 1]; 
##   } 
##   for (i in 2:K_ddimer) { 
##     means_X_ddimer[i - 1] = mean(X_ddimer[, i]); 
##     Xc_ddimer[, i - 1] = X_ddimer[, i] - means_X_ddimer[i - 1]; 
##   } 
##   for (i in 2:K_treat) { 
##     means_X_treat[i - 1] = mean(X_treat[, i]); 
##     Xc_treat[, i - 1] = X_treat[, i] - means_X_treat[i - 1]; 
##   } 
##   for (i in 2:K_death) { 
##     means_X_death[i - 1] = mean(X_death[, i]); 
##     Xc_death[, i - 1] = X_death[, i] - means_X_death[i - 1]; 
##   } 
## } 
## parameters { 
##   vector[Kc_pe] b_pe;  // population-level effects 
##   real temp_pe_Intercept;  // temporary intercept 
##   vector[Kc_angio] b_angio;  // population-level effects 
##   real temp_angio_Intercept;  // temporary intercept 
##   vector[Kc_ddimer] b_ddimer;  // population-level effects 
##   real temp_ddimer_Intercept;  // temporary intercept 
##   real<lower=0> sigma_ddimer;  // residual SD 
##   vector[Kc_treat] b_treat;  // population-level effects 
##   real temp_treat_Intercept;  // temporary intercept 
##   vector[Kc_death] b_death;  // population-level effects 
##   real temp_death_Intercept;  // temporary intercept 
## } 
## transformed parameters { 
## } 
## model { 
##   vector[N] mu_pe = Xc_pe * b_pe + temp_pe_Intercept; 
##   vector[N] mu_angio = Xc_angio * b_angio + temp_angio_Intercept; 
##   vector[N] mu_ddimer = Xc_ddimer * b_ddimer + temp_ddimer_Intercept; 
##   vector[N] mu_treat = Xc_treat * b_treat + temp_treat_Intercept; 
##   vector[N] mu_death = Xc_death * b_death + temp_death_Intercept; 
##   // priors including all constants 
##   target += student_t_lpdf(temp_pe_Intercept | 3, 0, 10); 
##   target += student_t_lpdf(temp_angio_Intercept | 3, 0, 10); 
##   target += student_t_lpdf(temp_ddimer_Intercept | 3, 221, 39); 
##   target += student_t_lpdf(sigma_ddimer | 3, 0, 39)
##     - 1 * student_t_lccdf(0 | 3, 0, 39); 
##   target += student_t_lpdf(temp_treat_Intercept | 3, 0, 10); 
##   target += student_t_lpdf(temp_death_Intercept | 3, 0, 10); 
##   // likelihood including all constants 
##   if (!prior_only) { 
##     target += bernoulli_logit_lpmf(Y_pe | mu_pe); 
##     target += bernoulli_logit_lpmf(Y_angio | mu_angio); 
##     target += normal_lpdf(Y_ddimer | mu_ddimer, sigma_ddimer); 
##     target += bernoulli_logit_lpmf(Y_treat | mu_treat); 
##     target += bernoulli_logit_lpmf(Y_death | mu_death); 
##   } 
## } 
## generated quantities { 
##   // actual population-level intercept 
##   real b_pe_Intercept = temp_pe_Intercept - dot_product(means_X_pe, b_pe); 
##   // actual population-level intercept 
##   real b_angio_Intercept = temp_angio_Intercept - dot_product(means_X_angio, b_angio); 
##   // actual population-level intercept 
##   real b_ddimer_Intercept = temp_ddimer_Intercept - dot_product(means_X_ddimer, b_ddimer); 
##   // actual population-level intercept 
##   real b_treat_Intercept = temp_treat_Intercept - dot_product(means_X_treat, b_treat); 
##   // actual population-level intercept 
##   real b_death_Intercept = temp_death_Intercept - dot_product(means_X_death, b_death); 
## }

Kết quả thô của mạng mô hình đa biến được tóm tắt bằng hàm summary như sau: Dù sử dụng quy luật phân bố hơi khác (bernoulli thay vì binomial), các tham số trong mô hình có thể xem như tương đương với kết quả của hàm glm cho từng mô hình đơn lẻ.

tên các effects trong kết quả được trình bày theo quy ước: (tên mô hình_hiệu ứng), thí dụ death_peYes có nghĩa là tham số cho hiệu ứng Pe=Yes trong mô hình tiên lượng nguy cơ tử vong.

summary(k_fit_brms)
##  Family: MV(bernoulli, bernoulli, gaussian, bernoulli, bernoulli) 
##   Links: mu = logit
##          mu = logit
##          mu = identity; sigma = identity
##          mu = logit
##          mu = logit 
## Formula: pe ~ wells 
##          angio ~ pe 
##          d.dimer ~ pregnant + pe 
##          treat ~ angio + d.dimer 
##          death ~ pe + treat 
##    Data: PE (Number of observations: 10000) 
## Samples: 1 chains, each with iter = 3000; warmup = 1000; thin = 2;
##          total post-warmup samples = 1000
## 
## Population-Level Effects: 
##                     Estimate Est.Error l-95% CI u-95% CI Eff.Sample Rhat
## pe_Intercept           -3.90      0.08    -4.06    -3.74       1000 1.00
## angio_Intercept        -2.22      0.04    -2.30    -2.16        902 1.00
## ddimer_Intercept      210.25      0.34   209.59   210.87       1000 1.00
## treat_Intercept        -5.90      0.16    -6.21    -5.59       1000 1.00
## death_Intercept        -4.19      0.10    -4.39    -4.00        910 1.00
## pe_wells                0.58      0.02     0.54     0.61       1000 1.00
## angio_peYes             3.28      0.07     3.15     3.42       1000 1.00
## ddimer_pregnantYes     29.28      0.99    27.36    31.16        795 1.01
## ddimer_peYes           68.37      0.73    66.98    69.77        986 1.00
## treat_angioPositive     1.73      0.06     1.61     1.85        990 1.00
## treat_d.dimer           0.02      0.00     0.02     0.02       1000 1.00
## death_peYes             5.48      0.14     5.22     5.77        947 1.00
## death_treatYes         -1.94      0.12    -2.17    -1.71        932 1.00
## 
## Family Specific Parameters: 
##              Estimate Est.Error l-95% CI u-95% CI Eff.Sample Rhat
## sigma_ddimer    29.88      0.20    29.47    30.26        859 1.00
## 
## Samples were drawn using sampling(NUTS). For each parameter, Eff.Sample 
## is a crude measure of effective sample size, and Rhat is the potential 
## scale reduction factor on split chains (at convergence, Rhat = 1).

Nhi trích xuất các chuỗi MCMC cho một số hiệu ứng cần khảo sát, và tính Odds ratio cho các hiệu ứng thuộc mô hình logistic bằng hàm exp(), sau đó vẽ biểu đồ phân phối hậu nghiệm và diễn tiến chuỗi MCMC như sau:

postdf=k_fit_brms$fit%>%as.data.frame()%>%
  as_tibble()%>%
  mutate(Iteration=as.numeric(c(1:1000)))

postdf[,c(1,2,4:7,10:13)]<-exp(postdf[,c(1,2,4:7,10:13)])

longpost=postdf%>%
  gather(b_pe_wells:b_death_treatYes,
         key="Variable",value="effect")

library(ggridges)

ggplot(longpost)+
  geom_density(aes(x=effect,fill=Variable),col="black",show.legend = F,alpha=0.5)+
  facet_wrap(~Variable,ncol=2,scales = "free")+
  scale_fill_manual(values=pals::tol(8))+theme_bw()

ggplot(longpost)+
  geom_path(aes(y=effect,x=Iteration,col=Variable),
            show.legend = F,alpha=0.8)+
  facet_wrap(~Variable,ncol=2,scales = "free")+
  scale_color_manual(values=pals::tol(8))+theme_bw()

Phân phối hậu nghiệm cho các điều kiện/hiệu ứng này hoàn toàn hợp lý, và có thể diễn giải như thường lệ, thí dụ: bệnh nhân bị thuyên tắc phổi có ddimer tăng trung bình 68 đơn vị, nguy cơ tử vong cao đến 240 lần. Việc điều trị làm giảm nguy cơ tử vong với Odds-ratio = 0.14, …

longpost%>%group_by(Variable)%>%
  summarise_at("effect",funs(Median=median,
                           SD=sd,
                           LL=quantile(.,probs=0.025),
                           UL=quantile(.,probs=0.975)))

Trước khi kết thúc, Nhi làm thêm một thủ thuật nhỏ nữa , để trình bày nội dung mô hình mạng dưới dạng một sơ đồ DAG có kèm Odds-ratio và hiệu ứng của mỗi mô hình:

5 Kết luận

Bài thực hành đến đây là hết, chúng ta vừa thử nghiệm tính năng mô hình cấu trúc đa biến của package brms. Tính năng này khá lợi hại, vì nó cho phép chúng ta lai ghép giữa thống kê Bayes và những phương pháp mô hình cấu trúc khác như SEM, DAG, Latent var. analysis, network … với tính linh hoạt rất lớn, thậm chí hơn cả những package sử dụng JAGs sampler.

Về giá trị thực tiễn, phương pháp mạng mô hình Bayes (Bayesian network)mở ra nhiều ứng dụng tiềm năng cho nghiên cứu Y học lâm sàng, vì cho phép các bạn kể một câu chuyện hấp dẫn và hợp lý với dữ liệu hiện có, suy diễn nhân quả, biện luận quy trình đưa ra quyết định chẩn đoán, phác đồ điều trị…bằng kết quả phân tích đa biến,dựa vào lý thuyết y học và tất cả những ưu thế mà trường phái Bayes có thể mang lại.

Chúc các bạn thành công.

LS0tDQp0aXRsZTogIk3huqFuZyBtw7QgaMOsbmggQkFZRVMiDQphdXRob3I6ICJMw6ogTmfhu41jIEto4bqjIE5oaSINCmRhdGU6ICIyMyBUaMOhbmcgNyAyMDE4Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdGhlbWU6ICJkZWZhdWx0Ig0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRvY19kZXB0aDogMw0KICAgIGRldjogJ3N2ZycNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiFbXShicm1zbmV0MS5wbmcpDQoNCiMgR2nhu5tpIHRoaeG7h3UNCg0KVGh1ecOqbiB04bqvYyBwaOG7lWkgbMOgIHTDrG5oIHRy4bqhbmcgbeG7mXQgaGF5IG5oaeG7gXUgxJHhu5luZyBt4bqhY2ggdHJvbmcgcGjhu5VpIGLhu4sgbmdo4bq9biBi4bufaSBt4buZdCBj4bulYyBtw6F1IMSRw7RuZyBkaSBjaHV54buDbiB04burIG3hu5l0IHBo4bqnbiBraMOhYyBj4bunYSBjxqEgdGjhu4MsIG5oxrAgY2hpIGTGsOG7m2kgKGh1eeG6v3Qga2jhu5FpIHTEqW5oIG3huqFjaCBzw6J1IChEVlQpKS4gVGh1ecOqbiB04bqvYyBwaOG7lWkgY8OzIHRo4buDIGfDonkgdOG7rSB2b25nIG7hur91IGLhu4duaCBuaMOibiBraMO0bmcgxJHGsOG7o2MgY2jhuqluIMSRb8OhbiB2w6AgxJFp4buBdSB0cuG7iyBr4buLcCB0aOG7nWkuIENo4bulcCDEkeG7mW5nIG3huqFjaCBwaOG7lWkgduG7m2kgdGh14buRYyBj4bqjbiBxdWFuZyBsw6AgeMOpdCBuZ2hp4buHbSBjaMOtbmggeMOhYyBuaOG6pXQgxJHhu4MgY2jhuqluIMSRb8OhbiB04bqvYyBt4bqhY2ggcGjhu5VpLCBuaMawbmcgY8OzIHTDrW5oIHjDom0gbOG6pW4gdsOgIG5ndXkgY8ahIGNhbyBuw6puIGNo4buJIMSRxrDhu6NjIHRo4buxYyBoaeG7h24ga2hpIGPDoWMgeMOpdCBuZ2hp4buHbSBraMOhYyBraMO0bmcgY3VuZyBj4bqlcCDEkeG7pyBi4bqxbmcgY2jhu6luZy5MxrDhu6NuZyBELWRpbWVyIHRyb25nIG3DoXUgdMSDbmcgY8OzIHRo4buDIGfhu6NpIMO9IHbhu4Egc+G7sSBoaeG7h24gZGnhu4duIGPhu6dhIGPhu6VjIG3DoXUgxJHDtG5nLCB0dXkgbmhpw6puIEQtZGltZXIgY8WpbmcgY8OzIHRo4buDIHTEg25nIGLhu59pIGPDoWMgeeG6v3UgdOG7kSBraMOhYywgbmjGsCBwaOG6q3UgdGh14bqtdCwgbWFuZyB0aGFpLg0KDQohW10oaHR0cHM6Ly93d3cubWF5b2NsaW5pYy5vcmcvLS9tZWRpYS9rY21zL2dicy9wYXRpZW50LWNvbnN1bWVyL2ltYWdlcy8yMDEzLzExLzE1LzE3LzM4L2RzMDAyMjNfLWRzMDA0MjlfLWRzMDEwMDVfaW0wMTI3Nl9tY2RjN19wdWxtb25hcnllbWJvbGlzbXRodV9qcGcuanBnKQ0KDQrEkOG6p3UgdGnDqm4sIGNow7puZyB0YSB0csOtY2ggeHXhuqV0IGLhu5kgZOG7ryBsaeG7h3UgUEUgdOG7qyBwYWNrYWdlIEh5ZGVOZXQsIGPhuqduIGdoaSBjaMO6IMSRw6J5IGNo4buJIGzDoCBt4buZdCBk4buvIGxp4buHdSBtw7QgcGjhu49uZyB24bubaSBt4bulYyB0acOqdSBtaW5oIGjhu41hIGNo4bupIGtow7RuZyBk4buxYSB0csOqbiBuZ2hpw6puIGPhu6l1IGPDsyB0aOG7sWMuIA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShIeWRlTmV0KQ0KDQpkYXRhKFBFKQ0KDQpQRSU+JWhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyKFBFKQ0KYGBgDQoNCk5oxrAgdGEgdGjhuqV5LCBQRSBsw6AgMSBkYXRhZnJhbWUgduG7m2kgMTAwMDAgdHLGsOG7nW5nIGjhu6NwIChi4buHbmggbmjDom4pLCB24bubaSA3IGJp4bq/biBs4bqnbiBsxrDhu6N0Y28mIG7hu5lpIGR1bmc6DQoNCjEpIHdlbGxzIGzDoCAxIGJp4bq/biBz4buRIHLhu51pIHLhuqFjLCB0csOsbmggYsOgeSDEkWnhu4NtIGzDom0gc8OgbmcgdGhlbyB0acOqdSBjaMOtIFdlbGxzLiANCg0KMikgcHJlZ25hbnQgbMOgIDEgYmnhur9uIG5o4buLIHBow6JuLCBjaOG7iSB0w6xuaCB0cuG6oW5nIEPDsy9raMO0bmcgbWFuZyB0aGFpLg0KDQozKSBwZSBsw6AgMSBiaeG6v24gbmjhu4sgcGjDom4sIGNobyBiaeG6v3QgY2jhuqluIMSRb8OhbiBj4bunYSBi4buHbmggdGh1eeG6v24gdOG6r2MgcGjhu5VpLg0KDQo0KSBhbmdpbyBsw6AgMSBiaeG6v24gbmjhu4sgcGjDom4sIHRyw6xuaCBiw6B5IGvhur90IHF14bqjIHjDqXQgbmdoaeG7h20geOG6oSBow6xuaCDEkeG7mW5nIG3huqFjaCBwaOG7lWkgduG7m2kgdGh14buRYyBj4bqjbiBxdWFuZywgduG7m2kgMiBnacOhIHRy4buLIFBvc2l0aXZlL05lZ2F0aXZlLg0KDQo1KSBkLmRpbWVyIGzDoCBiaeG6v24gc+G7kSBsacOqbiB04bulYywgdHLDrG5oIGLDoHkga+G6v3QgcXXhuqMgeMOpdCBuZ2hp4buHbSDEkeG7i25oIGzGsOG7o25nIEQuZGltZXIgdHJvbmcgbcOhdS4NCg0KNikgdHJlYXQgbMOgICAxIGJp4bq/biBuaOG7iyBwaMOibiwgbMOgIHF1eeG6v3QgxJHhu4tuaCDEkWnhu4F1IHRy4buLIDogQ8OzL2tow7RuZy4NCg0KNykgZGVhdGggbMOgIDEgYmnhur9uIG5o4buLIHBow6JuLCBjaG8gYmnhur90IGvhur90IGPhu6VjIGPhu6dhIGLhu4duaCBuaMOibjogTm89c+G7kW5nIHPDs3QsIFllcz1U4butIHZvbmcNCg0KIyBCxrDhu5tjIDE6IFRoaeG6v3Qga+G6vyBj4bqldSB0csO6YyBt4bqhbmcgbcO0IGjDrG5oDQoNClbhu5tpIGThu68gbGnhu4d1IGhp4buHbiB0aOG7nWksIHLhuqV0IG5oaeG7gXUgaMaw4bubbmcgbmdoacOqbiBj4bupdSBjw7MgdGjhu4MgxJHhurd0IHJhLCB24bubaSBjw6FjIGdp4bqjIHRodXnhur90L2PDonUgaOG7j2kga2jDoWMgbmhhdS4gVGjDrSBk4bulOiBUYSBjw7MgdGjhu4MgZOG7sW5nIG3hu5l0IG3DtCBow6xuaCBuaOG6sW0gdGnDqm4gbMaw4bujbmcgbmd1eSBjxqEgdGh1ecOqbiB04bqvYyBwaOG7lWkgKGvhur90IHF14bqjID0gYmnhur9uIHBlKSBk4buxYSB2w6BvIGPDoWMgdGjDtG5nIHRpbiBsw6JtIHPDoG5nICjEkWnhu4NtIHdlbGxzLCBr4bq/dCBxdeG6oyB4w6l0IG5naGnhu4dtIGQuZGltZXIsIGFuZ2lvZ3JhcGh5KTsgdGEgY8WpbmcgY8OzIHRo4buDIG5o4bqvbSDEkeG6v24ga+G6v3QgY+G7pWMgInThu60gdm9uZyIgY+G7p2EgYuG7h25oIG5ow6JuIHbDoCBraOG6o28gc8OhdCBt4buRaSBsacOqbiBo4buHIGdp4buvYSBr4bq/dCBj4bulYyBuw6B5IHbDoCBjaOG6qW4gxJFvw6FuIChwZSkgdsOgIGNo4buJIMSR4buLbmggxJFp4buBdSB0cuG7iyAodHJlYXQpO0PFqW5nIGPDsyB0aOG7gyB0YSBtdeG7kW4ga2jhuqNvIHPDoXQgc+G7sSDhuqNuaCBoxrDhu59uZyBj4bunYSBr4bq/dCBxdeG6oyBjw6FjIHjDqXQgbmdoaeG7h20gdsOgIMSRaeG7g20gd2VsbHMgduG7m2kgcXV54bq/dCDEkeG7i25oIMSRaeG7gXUgdHLhu4sgY+G7p2EgYsOhYyBzxKkuLi4gDQoNClR1eSBuaGnDqm4sIHRyb25nIGLDoGkgdGjhu7FjIGjDoG5oIGjDtG0gbmF5IE5oaSBz4bq9IGPDuW5nIGPDoWMgYuG6oW4gxJHhurd0IHJhIG3hu5l0IGPDoWNoIHRp4bq/cCBj4bqtbiB24bqlbiDEkeG7gSB0b8OgbiBkaeG7h24gdsOgIHBo4bupYyB04bqhcCBoxqFuLCB24bubaSBt4bulYyB0acOqdS/DvSB0xrDhu59uZzoNCg0KROG7sW5nIG3DtCBow6xuaCBjaG8gcGjDqXAga2jhuqNvIHPDoXQgxJHGsOG7o2MgdG/DoG4gYuG7mSAobeG6oW5nIGzGsOG7m2kpIG5o4buvbmcgbeG7kWkgbGnDqm4gaOG7hyAvIOG6o25oIGjGsOG7n25nIGto4bqjIGTEqSBnaeG7r2EgY8OhYyB54bq/dSB04buROiBC4buHbmggbMO9IChwZSksIGvhur90IHF14bqjIHjDqXQgbmdoaeG7h20gY+G6rW4gbMOibSBzw6BuZyAoZC5kaW1lciwgYW5naW8pLCBj4bqjbSBuaOG6rW4gY2jhu6cgcXVhbiAod2VsbHMpLCBxdXnhur90IMSR4buLbmggxJFp4buBdSB0cuG7iyAodHJlYXQpLCB2w6Aga+G6v3QgY+G7pWMgbMOibSBzw6BuZyAobmd1eSBjxqEgdOG7rSB2b25nKSBj4bunYSBi4buHbmggbmjDom4uIEvhur90IHF14bqjIG3DtCBow6xuaCBiYW8gZ+G7k20gY2hp4buBdSBoxrDhu5tuZy9rw61jaCB0aMaw4bubYyBj4bunYSBt4buXaSBoaeG7h3Ug4bupbmcgL2xpw6puIGjhu4cuDQoNCk3hu6VjIHRpw6p1IG7DoHkgY8OzIHRo4buDIGhp4buDdSBuaMawOiB0w7RpIG114buRbiBkw7luZyBwaMOibiB0w61jaCB0aOG7kW5nIGvDqiDEkeG7gyBzdXkgZGnhu4VuIG5ow6JuL3F14bqjOiAgZ2nhu69hIGPDoWMgeeG6v3UgdOG7kSwgdsOgIHTDtGkgbXXhu5FuIGvhu4MgbeG7mXQgY8OidSBjaHV54buHbiBjw7Mgw70gbmdoxKlhIGThu7FhIHRyw6puIGThu68gbGnhu4d1IG7DoHkuDQoNCktow6FjIHbhu5tpIG5o4buvbmcgcGjGsMahbmcgcGjDoXAgcGjDom4gdMOtY2ggIG3DtCBow6xuaCDEkWEgYmnhur9uIHF1eSDGsOG7m2MgLSB24buRbiB0aMaw4budbmcgcXXDoSBjaMO6IHRy4buNbmcgduG7gSB0aOG7kW5nIGvDqiwgdGjDrSBk4bulIGNo4buNbiBs4buNYyBtw7QgaMOsbmggLyBiaeG6v24gc+G7kSBk4buxYSB2w6BvIGPDoWMgY2jhu4kgc+G7kSBCSUMsIEFJQyAoU3RlcHdpc2UpIGhv4bq3YyBCTUEsIGPDoWMgcGjGsMahbmcgcGjDoXAgcsO6dCBn4buNbiBtw7QgaMOsbmggbmjGsCBMYXNzby4uLiwgdHJvbmcgYsOgaSB0b8OhbiBuw6B5IGNow7puZyB0YSBz4bq9IMawdSB0acOqbiB4w6J5IGThu7FuZyBt4bqhbmcgbMaw4bubaSB0xrDGoW5nIHTDoWMgZ2nhu69hIGPDoWMgeeG6v3UgdOG7kSBt4buZdCBjw6FjaCBjaOG7pyBxdWFuLCBk4buxYSB2w6BvIGtp4bq/biB0aOG7qWMgeSBo4buNYywga2luaCBuZ2hp4buHbSB2w6AgcXV5IGx14bqtdCBo4bujcCBsw70ga2jDoWMuIA0KDQpUYSBjw7MgdGjhu4MgYuG6r3QgxJHhuqd1IHZp4buHYyBuw6B5IG5oxrAgc2F1Og0KDQpUcsaw4bubYyBo4bq/dCwgbeG6oW5nIGzGsOG7m2kgbcO0IGjDrG5oIGPhu6dhIGNow7puZyB0YSBjw7MgNyBuw7p0ICwgdHJvbmcgxJHDsyAyIG7DunQgcXVhbiB0cuG7jW5nIG5o4bqldCBsw6AgQ2jhuqluIMSRb8OhbiAocGUpIHbDoCDEkGnhu4F1IHRy4buLICh0cmVhdCksIG7DunQgdOG6rW4gY8O5bmcgbMOgIGvhur90IGPhu6VjOiBz4buRbmcgc8OzdCBoYXkgdOG7rSB2b25nIChkZWF0aCkuDQoNCuG7niB0aOG7nWkgxJFp4buDbSBuaOG6rXAgdmnhu4duLCBjaMawYSBjw7MgeMOpdCBuZ2hp4buHbSBuw6BvIMSRxrDhu6NjIGzDoG0sIGLhu4duaCBuaMOibiDEkcaw4bujYyBraGFpIHRow6FjIGLhu4duaCBz4butIHbDoCBraMOhbSBi4bufaSBiw6FjIHPEqSwgZG8gxJHDsyDEkWnhu4NtIGto4bufaSDEkeG6p3UgY+G7p2EgbeG6oW5nIGzGsOG7m2kgY2jDrW5oIGzDoCBuw7p0IHdlbGxzLiBOaMawIHRhIGJp4bq/dCwgV2VsbHMgbMOgIG3hu5l0IHRoYW5nIMSRaeG7g20gbMOibSBzw6BuZyB0acOqbiBsxrDhu6NuZyBuZ3V5IGPGoSB0aHV5w6puIHThuq9jIHBo4buVaSwgZOG7sWEgdsOgbyBjw6FjIHRpw6p1IGNow60gbmjGsCBi4buHbmggc+G7rSAoYuG6pXQgxJHhu5luZyBrw6lvIGTDoGksIHBo4bqrdSB0aHXhuq10LCBi4buHbmggbMO9IHTEqW5oIG3huqFjaCwgxJFp4buBdSB0cuG7iyB1bmcgdGjGsCksIHRyaeG7h3UgY2jhu6luZyAoZOG6pXUgaGnhu4d1IGh1eeG6v3Qga2jhu5FpIHTEqW5oIG3huqFjaCwgaG8gcmEgbcOhdSwgbmjhu4twIHRpbSBuaGFuaC4uLikuIFRhIGPDsyB0aOG7gyBuw7NpIGNo4bqpbiDEkW/DoW4gbMOibSBzw6BuZyAocGUpIHBo4bulIHRodeG7mWMgdGhhbmcgxJFp4buDbSBXZWxscywgdOG7qyDEkcOzIHbhuqFjaCByYSBsacOqbiBr4bq/dCDEkeG6p3UgdGnDqm4gdHJvbmcgbeG6oW5nIGzGsOG7m2k6IHBlIHwgd2VsbHMuIFRhIGPDsyB0aOG7gyB0csOsbmggYsOgeSBxdWFuIGjhu4cgxJFp4buBdSBraeG7h24gbsOgeSBi4bqxbmcgbeG7mXQgbcO0IGjDrG5oIGxvZ2lzdGljIMSRxqFuIGJp4bq/bi4NCg0KVGnhur9wIHRoZW8sIHRhIHjDqXQgbsO6dCBELmRpbWVyIG5oxrAgYmnhur9uIGvhur90IHF14bqjLiBDw7MgdGjhu4MgbsOzaSBnacOhIHRy4buLIGQuZGltZXIgcGjhu6UgdGh14buZYyB2w6BvIHTDrG5oIHRy4bqhbmcgY8OzIHRodXnDqm4gdOG6r2MgcGjhu5VpIGhheSBraMO0bmcsIGRvIMSRw7MgY8OzIHF1YW4gaOG7hyBnaeG7r2EgZC5kaW1lciB2w6AgcGU7IHR1eSBuaGnDqm4ga+G6v3QgcXXhuqMgeMOpdCBuZ2hp4buHbSBkLmRpbWVyIGPDsyB0aOG7gyBi4buLIHNhaSBs4buHY2gga2hpIGPDsyB0aGFpIChwcmVnbmFudCA9IFllcywgduG7m2kgbmd1eSBjxqEgZMawxqFuZyB0w61uaCBnaeG6oywgZG8gxJHDsyBj4bqjIHBlIHbDoCBwcmVnbmFudCBjw7luZyB0aGFtIGdpYSB2w6BvIG3DtCBow6xuaCB0acOqbiBsxrDhu6NuZyBkLmRpbWVyLiANCg0Ka+G6v3QgcXXhuqMgeMOpdCBuZ2hp4buHbSBBbmdpb2dyYXBoeSBjaOG7iSBwaOG7pSB0aHXhu5ljIHbDoG8gc+G7sSBoaeG7h24gZGnhu4duIHRodXnDqm4gdOG6r2MgcGjhu5VpIChwZSkgaGF5IGtow7RuZywgbsOqbiBjaOG7iSBjw7MgMSBsacOqbiBr4bq/dCB04burIHBlIMSR4bq/biBhbmdpby4NCg0KVGnhur9wIHRoZW8sIHRhIHjDqXQgxJHhur9uIG7DunQ6IGNo4buJIMSR4buLbmggxJFp4buBdSB0cuG7iyAodHJlYXQpOiBC4buHbmggbmjDom4gY2jhu4kgxJHGsOG7o2MgxJFp4buBdSB0cuG7iyBraGkgY8OzIHjDqXQgbmdoaeG7h20gZC5kaW1lciB2w6AvaG/hurdjIGFuZ2lvIGTGsMahbmcgdMOtbmgsIHjDoWMgxJHhu4tuaCBjaOG6qW4gxJFvw6FuOyBkbyDEkcOzIHRhIGPDsyB0aOG7gyBkw7luZyBt4buZdCBtw7QgaMOsbmggbG9naXN0aWMga2jDoWMgduG7m2kgMiBiaeG6v24gbMOgIGFuZ2lvIHbDoCBkLmRpbWVyLg0KDQpDdeG7kWkgY8O5bmcsIG7DunQgdOG6rW4gY8O5bmcgY+G7p2EgbeG6oW5nIGzGsOG7m2kgbMOgIGvhur90IGPhu6VjIHThu60gdm9uZyBj4bunYSBi4buHbmggbmjDom4sIMSRw6J5IGzDoCBr4bq/dCBxdeG6oyBj4butYSBz4buxIHTGsMahbmcgdMOhYyBnaeG7r2EgMiDEkWnhu4F1IGtp4buHbjogYuG7h25oIGzDvSB0aHV5w6puIHThuq9jIHBo4buVaSAocGUpIHbDoCBjaOG7iSDEkeG7i25oIMSRaeG7gXUgdHLhu4sgKHRyZWF0KS4gVGjDrSBk4bulOiDhu58gYuG7h25oIG5ow6JuIGPDsyB0aHV5w6puIHThuq9jIHBo4buVaSAsIG5ndXkgY8ahIHThu60gdm9uZyBjw7MgdGjhu4MgdGjhuqVwIGjGoW4gbuG6v3UgxJHGsOG7o2MgxJFp4buBdSB0cuG7iyBr4buLcCB0aOG7nWksIG5nxrDhu6NjIGzhuqFpIG5ndXkgY8ahIHThu60gdm9uZyBjYW8gbuG6v3Uga2jDtG5nIMSRxrDhu6NjIMSRaeG7gXUgdHLhu4suDQoNClNhdSBraGkgcGjDoWMgdGjhuqNvIG3huqFuZyBtw7QgaMOsbmggdHLDqm4gZ2nhuqV5LCBOaGkgZMO5bmcgcGFja2FnZSBIeWRlTmV0IMSR4buDIHRoaeG6v3Qga+G6vyBj4bqldSB0csO6YyBj4bunYSBt4bqhbmcgbMaw4bubaSBtw7QgaMOsbmggdsOgIHbhur0gc8ahIMSR4buTIDoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCm5ldCA8LSBIeWRlTmV0d29yayh+IHdlbGxzDQogICAgICAgICAgICAgICAgICAgKyBwZSB8IHdlbGxzDQogICAgICAgICAgICAgICAgICAgKyBkLmRpbWVyIHwgcHJlZ25hbnQqcGUNCiAgICAgICAgICAgICAgICAgICArIGFuZ2lvIHwgcGUNCiAgICAgICAgICAgICAgICAgICArIHRyZWF0IHwgZC5kaW1lciphbmdpbw0KICAgICAgICAgICAgICAgICAgICsgZGVhdGggfCBwZSp0cmVhdCkNCg0KSHlkZVBsb3RPcHRpb25zKHZhcmlhYmxlPWxpc3QoZmlsbGNvbG9yID0gImdvbGQiKSkNCg0KcGxvdChuZXQpDQpgYGANCg0KQsOqbiB0cm9uZyBvYmplY3QgbmV0IG7DoHkgY8OzIGNo4bupYSBz4bq1biBt4buZdCBtYSB0cuG6rW4gREFHIChEaXJlY3RlZCBBY3ljbGljIGdyYXBoKSwgTmhpIG114buRbiBi4bqtdCBtw60gY2hvIGPDoWMgYuG6oW4sIHbDrCB04burIG1hdHJpeCBEQUcgbsOgeSB0YSBjw7MgdGjhu4MgduG6vSByYSBzxqEgxJHhu5MgbeG6oW5nIHTDuXkgdGjDrWNoIGLhurFuZyBwYWNrYWdlIGlncmFwaCB2w6AgZ2dyYXBoLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCm5ldCRkYWcNCmBgYA0KDQojIELGsOG7m2MgMjogQ2h1eeG7g24gc8ahIMSR4buTIG3huqFuZyB0aMOgbmggbeG6oW5nIG3DtCBow6xuaCBCYXllcw0KDQpE4buxYSB2w6BvIHPGoSDEkeG7kyBt4bqhbmcgbcO0IGjDrG5oIG5oxrAgdHLDqm4sIHRhIGPDsyB0aOG7gyBow6xuaCBkdW5nIHbhu4EgbsOzIG5oxrAgbeG7mXQgdOG6rXAgaOG7o3AgNSBtw7QgaMOsbmggdMawxqFuZyDhu6luZyB24bubaSA1IG7DunQga+G6v3QgcXXhuqM6IHBlLCBhbmdpbywgZC5kaW1lciwgdHJlYXQgdsOgIGRlYXRoLiBOaMawIHbhuq15IG7hur91IHRhIHjDoWMgxJHhu4tuaCB0aMOgbmggY8O0bmcgNSBtw7QgaMOsbmggY29uIG7DoHksIHhlbSBuaMawIG3hu6VjIHRpw6p1IMSRw6MgxJHhuqF0IMSRxrDhu6NjLg0KDQojIyBQaMOibiB0w61jaCBo4buTaSBxdXkgduG7m2kgaMOgbSBHTE0gey50YWJzZXR9DQoNCiMjIyBQRQ0KDQpNw7QgaMOsbmggY2hvIGxpw6puIGvhur90IFdlbGxzIC0gUEUgY8OzIGThuqFuZyBt4buZdCBtw7QgaMOsbmggbG9naXN0aWMgxJHGoW4gYmnhur9uLCB24bubaSBnaeG6oyDEkeG7i25oIGvhur90IHF14bqjIHjDoWMgc3XhuqV0IFBFIMSRxrDhu6NjIMaw4bubYyB0w61uaCBi4bqxbmcgcGjDom4gYuG7kSBiaW5vbWlhbCwgbGluayBmdW5jdGlvbiBsw6AgaMOgbSBsb2dpdA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCg0KYmlub21pYWxfc21vb3RoIDwtIGZ1bmN0aW9uKC4uLikgew0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCANCiAgICAgICAgICAgICAgY29sPSJibGFjayIsZmlsbD0iZ29sZCIsbGluZXR5cGU9Mil9DQoNClBFJT4lbXV0YXRlKGRpYWc9YXMubnVtZXJpYyhwZSktMSklPiUNCiAgZ2dwbG90KGFlcyh4PXdlbGxzLHk9ZGlhZykpKw0KICBnZW9tX3BvaW50KGFlcyhjb2w9cGUpKSsNCiAgYmlub21pYWxfc21vb3RoKGZvcm11bGEgPSB5IH4geCxhbHBoYT0wLjMpKw0KICB0aGVtZV9idygpDQoNCg0KcGVfbG08LWdsbShwZSB+IHdlbGxzLCBmYW1pbHk9ImJpbm9taWFsIixkYXRhPVBFKQ0KDQpzdW1tYXJ5KHBlX2xtKQ0KDQpwZV9sbSU+JWJyb29tOjp0aWR5KCklPiUNCiAgbXV0YXRlKE9SPWV4cChlc3RpbWF0ZSksDQogICAgICAgICBVTD1leHAoZXN0aW1hdGUrMS45NipzdGQuZXJyb3IpLA0KICAgICAgICAgTEw9ZXhwKGVzdGltYXRlLTEuOTYqc3RkLmVycm9yKSklPiUNCiAgc2VsZWN0KHRlcm0sT1IsTEwsVUwscC52YWx1ZSkNCmBgYA0KDQojIyMgQW5naW8NCg0KTcO0IGjDrG5oIGNobyBsacOqbiBr4bq/dCBwZS1hbmdpbyBjxaluZyBsw6AgbeG7mXQgbcO0IGjDrG5oIGxvZ2lzdGljIMSRxqFuIGJp4bq/bjoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQphbmdpb19sbTwtZ2xtKGFuZ2lvIH4gcGUsIGZhbWlseT0iYmlub21pYWwiLGRhdGE9UEUpDQoNCnN1bW1hcnkoYW5naW9fbG0pDQoNCmFuZ2lvX2xtJT4lYnJvb206OnRpZHkoKSU+JQ0KICBtdXRhdGUoT1I9ZXhwKGVzdGltYXRlKSwNCiAgICAgICAgIFVMPWV4cChlc3RpbWF0ZSsxLjk2KnN0ZC5lcnJvciksDQogICAgICAgICBMTD1leHAoZXN0aW1hdGUtMS45NipzdGQuZXJyb3IpKSU+JQ0KICBzZWxlY3QodGVybSxPUixMTCxVTCxwLnZhbHVlKQ0KYGBgDQoNCiMjIyBELkRpbWVyDQoNCk3DtCBow6xuaCBjaG8gbGnDqm4ga+G6v3QgKGQuZGltZXIgfCBwcmVnbmFudCpwZSkgbMOgIG3hu5l0IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCwgduG7m2kgZ2nhuqMgxJHhu4tuaCBiaeG6v24ga+G6v3QgcXXhuqMgZC5kaW1lciDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgYuG6sW5nIHBow6JuIGLhu5EgR2F1c3NpYW4sIHBo4bulIHRodeG7mWMgdsOgbyAyIGJp4bq/biBwcmVnbmFudCB2w6AgcGU6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGRpbWVyX2xtPC1nbG0oZC5kaW1lciB+IHByZWduYW50ICsgcGUsZGF0YT1QRSkNCg0Kc3VtbWFyeShkZGltZXJfbG0pDQoNCmBgYA0KDQojIyMgVHJlYXQNCg0KTcO0IGjDrG5oIGNobyBsacOqbiBr4bq/dCAodHJlYXQgfCBkLmRpbWVyKmFuZ2lvKSBjw7MgZOG6oW5nIGxvZ2lzdGljLCAyIGJp4bq/biA6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQp0cmVhdF9sbTwtZ2xtKHRyZWF0IH4gYW5naW8gKyBkLmRpbWVyLGZhbWlseT0iYmlub21pYWwiLGRhdGE9UEUpDQoNCnN1bW1hcnkodHJlYXRfbG0pDQoNCnRyZWF0X2xtJT4lYnJvb206OnRpZHkoKSU+JQ0KICBtdXRhdGUoT1I9ZXhwKGVzdGltYXRlKSwNCiAgICAgICAgIFVMPWV4cChlc3RpbWF0ZSsxLjk2KnN0ZC5lcnJvciksDQogICAgICAgICBMTD1leHAoZXN0aW1hdGUtMS45NipzdGQuZXJyb3IpKSU+JQ0KICBzZWxlY3QodGVybSxPUixMTCxVTCxwLnZhbHVlKQ0KDQpgYGANCg0KIyMjIERlYXRoDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGVhdGhfbG08LWdsbShkZWF0aCB+IHBlICsgdHJlYXQsZmFtaWx5PSJiaW5vbWlhbCIsZGF0YT1QRSkNCiAgICAgICAgICAgICANCnN1bW1hcnkoZGVhdGhfbG0pDQoNCmRlYXRoX2xtJT4lYnJvb206OnRpZHkoKSU+JQ0KICBtdXRhdGUoT1I9ZXhwKGVzdGltYXRlKSwNCiAgICAgICAgIFVMPWV4cChlc3RpbWF0ZSsxLjk2KnN0ZC5lcnJvciksDQogICAgICAgICBMTD1leHAoZXN0aW1hdGUtMS45NipzdGQuZXJyb3IpKSU+JQ0KICBzZWxlY3QodGVybSxPUixMTCxVTCxwLnZhbHVlKQ0KYGBgDQoNCiMjIE3huqFuZyBtw7QgaMOsbmggQmF5ZXMgc+G7rSBk4bulbmcgYnJtcyB2w6AgU1RBTg0KDQpCw6J5IGdp4budLCBOaGkgc+G6vSBk4buxbmcgbeG7mXQgbeG6oW5nIG3DtCBow6xuaCBCYXllcyB0aOG7sWMgc+G7sSBkw7luZyBuZ8O0biBuZ+G7ryBTVEFOIHF1YSBnaWFvIHRo4bupYyBsw6AgcGFja2FnZSBicm1zLg0KDQpUcm9uZyBjw6FjIGLDoGkgdHLGsOG7m2MsIE5oaSDEkcOjIHThu6tuZyBnaeG7m2kgdGhp4buHdSBjaG8gY8OhYyBi4bqhbiBwYWNrYWdlIGJybXMgbMOgIG3hu5l0IGPDtG5nIGPhu6UgcuG6pXQgbeG6oW5oIG3hur0sIGxpbmggaG/huqF0IGNobyBwaMOibiB0w61jaCBCYXllcy4gTeG7mXQgdHJvbmcgbmjhu69uZyB0w61uaCBuxINuZyBtw6AgYnJtcyBo4buXIHRy4bujIGzDoCBk4buxbmcgbcO0IGjDrG5oIG3huqFuZyBsxrDhu5tpIGPhuqV1IHRyw7pjIChi4bqhbiBjw7MgdGjhu4MgZ+G7jWkgcGjGsMahbmcgcGjDoXAgbsOgeSBi4bqxbmcgbmhp4buBdSBjw6FpIHTDqm46IFNFTSwgREFHLCBMYXRlbnQgdmFyaWFibGUgYW5hbHlzaXMgaGF5IEJheWVzaWFuIG5ldHdvcmsuLi4sIG5oxrBuZyBjw6FjaCB0aOG7sWMgaGnhu4duIGPFqW5nIG5oxrAgbmhhdSkuIFTDrW5oIG7Eg25nIG7DoHkgY8OzIGLhuqNuIGNo4bqldCBsw6AgxrDhu5tjIGzGsOG7o25nIChNQ01DIHNhbXBsaW5nKSDEkeG7k25nIHRo4budaSBjaG8gaMOgbmcgbG/huqF0IG3DtCBow6xuaCBu4bqxbSB0cm9uZyBt4buZdCBt4bqhbmcgbMaw4bubaSBj4bqldSB0csO6YyBwaOG7qWMgdOG6oXAuIGJybXMgY2hvIHBow6lwIMOhcCBk4bulbmcgcHJpb3JzLCBmYW1pbHkgdsOgIGPDtG5nIHRo4bupYyByacOqbmcgYmnhu4d0IGNobyBt4buXaSBtw7QgaMOsbmggYsOqbiB0cm9uZyBt4bqhbmcgbMaw4bubaSwgZG8gxJHDsyBo4bq/dCBz4bupYyBsaW5oIGhv4bqhdCBraGkgxJHhu5FpIGRp4buHbiB24bubaSBjw6FjIGLDoGkgdG/DoW4gcGjhu6ljIHThuqFwLg0KDQpUcm9uZyBiw6BpIHRo4buxYyBow6BuaCBuw6B5LCBOaGkgxJHGoW4gZ2nhuqNuIHbhuqVuIMSR4buBIGLhurFuZyBjw6FjaCBjaOG7iSDDoXAgZOG7pW5nIDIga2nhu4N1IG3DtCBow6xuaDogbG9naXN0aWMgdsOgIEdhdXNzaWFuLCB2w6Aga2jDtG5nIHTDuXkgY2jhu4luaCBzw6J1IHbhu4EgcHJpb3JzLg0KDQpUcsaw4bubYyBo4bq/dCwgdGEgdmnhur90IGPDtG5nIHRo4bupYyBjaG8gNSBtw7QgaMOsbmggdGhlbyBjw7ogcGjDoXAgYnJtczoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGJybXMpDQoNCnBlX21vZDwtYmYocGUgfiB3ZWxscywgZmFtaWx5PWJlcm5vdWxsaShsaW5rID0gImxvZ2l0IikpDQphbmdpb19tb2Q8LWJmKGFuZ2lvIH4gcGUsIGZhbWlseT1iZXJub3VsbGkobGluayA9ICJsb2dpdCIpKQ0KZGRpbWVyX21vZDwtYmYoZC5kaW1lciB+IHByZWduYW50ICsgcGUpDQp0cmVhdF9tb2Q8LWJmKHRyZWF0IH4gYW5naW8gKyBkLmRpbWVyLGZhbWlseT1iZXJub3VsbGkobGluayA9ICJsb2dpdCIpKQ0KZGVhdGhfbW9kPC1iZihkZWF0aCB+IHBlICsgdHJlYXQsZmFtaWx5PWJlcm5vdWxsaShsaW5rID0gImxvZ2l0IikpDQpgYGANCg0KU2F1IMSRw7MsIHRhIGTDuW5nIGjDoG0gYnJtIMSR4buDIMSRw7NuZyBnw7NpICB04bqldCBj4bqjIG3DtCBow6xuaCB0cm9uZyBjw7luZyAxIGNo4bq/IMSR4buZIGzhuqV5IG3huqt1IE1DTUM6IHbhu5tpIDEgY2h14buXaSwgMTAwMCBsxrDhu6N0IGto4bufaSDEkeG7mW5nLCAyMDAwIGzGsOG7o3QgbOG6pXkgbeG6q3UsIHNhdSDEkcOzIHLDunQgZ+G7jW4gNTAlLg0KDQpicm1zIHPhur0gY29tcGlsZSBtw7QgaMOsbmggdGjDoG5oIFNUQU4gY29kZSwgc2F1IMSRw7MgdGhpIGjDoG5oLiBUaeG6v24gdHLDrG5oIHTGsMahbmcgxJHhu5FpIGNo4bqtbSwgdMO5eSB2w6BvIG3DoXkgdMOtbmggbeG6oW5oIGhheSB54bq/dSAoTmhpIG3huqV0IGtob+G6o25nIDE1IHBow7p0KSwgbmjGsG5nIG3DtCBow6xuaCBy4buTaSBjxaluZyBz4bq9IGNvbnZlcmdlIHhvbmcuDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0Ka19maXRfYnJtcyA8LSBicm0ocGVfbW9kICsNCiAgICAgICAgICAgICAgICAgICAgYW5naW9fbW9kICsNCiAgICAgICAgICAgICAgICAgICAgZGRpbWVyX21vZCArDQogICAgICAgICAgICAgICAgICAgIHRyZWF0X21vZCArDQogICAgICAgICAgICAgICAgICAgIGRlYXRoX21vZCsNCiAgICAgICAgICAgICAgICAgICAgc2V0X3Jlc2NvcihGQUxTRSksDQogICAgICAgICAgICAgICAgICBkYXRhPVBFLA0KICAgICAgICAgICAgICAgICAgaXRlciA9IDMwMDAsIA0KICAgICAgICAgICAgICAgICAgd2FybXVwID0gMTAwMCwNCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDEsIA0KICAgICAgICAgICAgICAgICAgdGhpbiA9IDIsDQogICAgICAgICAgICAgICAgICByZWZyZXNoID0gMCwNCiAgICAgICAgICAgICAgICAgIGNvbnRyb2w9bGlzdChhZGFwdF9kZWx0YT0wLjk1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF90cmVlZGVwdGg9MjApKQ0KYGBgDQoNCiMgQsaw4bubYyAzOiBLaOG6o28gc8OhdCBu4buZaSBkdW5nIG3huqFuZyBtw7QgaMOsbmggQmF5ZXMNCg0KVHLGsOG7m2MgaOG6v3QsIE5oaSB0csOtY2ggbuG7mWkgZHVuZyBTVEFOIGNvZGUgY+G7p2EgbcO0IGjDrG5oIHbhu6thIGThu7FuZywgY29kZSBuw6B5IGNo4buJIG5o4bqxbSB0aOG7j2EgbcOjbiBz4buxIHTDsiBtw7IgY+G7p2EgY8OhYyBi4bqhbiB0aMOtY2ggaOG7jWMgdmnhur90IGNvZGUgdGjhu6cgY8O0bmcgYuG6sW5nIFNUQU4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzdGFuY29kZShrX2ZpdF9icm1zICkNCmBgYA0KDQpL4bq/dCBxdeG6oyB0aMO0IGPhu6dhIG3huqFuZyBtw7QgaMOsbmggxJFhIGJp4bq/biDEkcaw4bujYyB0w7NtIHThuq90IGLhurFuZyBow6BtIHN1bW1hcnkgbmjGsCBzYXU6IETDuSBz4butIGThu6VuZyBxdXkgbHXhuq10IHBow6JuIGLhu5EgaMahaSBraMOhYyAoYmVybm91bGxpIHRoYXkgdsOsIGJpbm9taWFsKSwgY8OhYyB0aGFtIHPhu5EgdHJvbmcgbcO0IGjDrG5oIGPDsyB0aOG7gyB4ZW0gbmjGsCB0xrDGoW5nIMSRxrDGoW5nIHbhu5tpIGvhur90IHF14bqjIGPhu6dhIGjDoG0gZ2xtIGNobyB04burbmcgbcO0IGjDrG5oIMSRxqFuIGzhursuDQoNCnTDqm4gY8OhYyBlZmZlY3RzIHRyb25nIGvhur90IHF14bqjIMSRxrDhu6NjIHRyw6xuaCBiw6B5IHRoZW8gcXV5IMaw4bubYzogKHTDqm4gbcO0IGjDrG5oX2hp4buHdSDhu6luZyksIHRow60gZOG7pSBkZWF0aF9wZVllcyBjw7MgbmdoxKlhIGzDoCB0aGFtIHPhu5EgY2hvIGhp4buHdSDhu6luZyBQZT1ZZXMgdHJvbmcgbcO0IGjDrG5oIHRpw6puIGzGsOG7o25nIG5ndXkgY8ahIHThu60gdm9uZy4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzdW1tYXJ5KGtfZml0X2JybXMpDQpgYGANCg0KTmhpIHRyw61jaCB4deG6pXQgY8OhYyBjaHXhu5dpIE1DTUMgY2hvIG3hu5l0IHPhu5EgaGnhu4d1IOG7qW5nIGPhuqduIGto4bqjbyBzw6F0LCB2w6AgdMOtbmggT2RkcyByYXRpbyBjaG8gY8OhYyBoaeG7h3Ug4bupbmcgdGh14buZYyBtw7QgaMOsbmggbG9naXN0aWMgYuG6sW5nIGjDoG0gZXhwKCksIHNhdSDEkcOzIHbhur0gYmnhu4N1IMSR4buTIHBow6JuIHBo4buRaSBo4bqtdSBuZ2hp4buHbSB2w6AgZGnhu4VuIHRp4bq/biBjaHXhu5dpIE1DTUMgbmjGsCBzYXU6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcG9zdGRmPWtfZml0X2JybXMkZml0JT4lYXMuZGF0YS5mcmFtZSgpJT4lDQogIGFzX3RpYmJsZSgpJT4lDQogIG11dGF0ZShJdGVyYXRpb249YXMubnVtZXJpYyhjKDE6MTAwMCkpKQ0KDQpwb3N0ZGZbLGMoMSwyLDQ6NywxMDoxMyldPC1leHAocG9zdGRmWyxjKDEsMiw0OjcsMTA6MTMpXSkNCg0KbG9uZ3Bvc3Q9cG9zdGRmJT4lDQogIGdhdGhlcihiX3BlX3dlbGxzOmJfZGVhdGhfdHJlYXRZZXMsDQogICAgICAgICBrZXk9IlZhcmlhYmxlIix2YWx1ZT0iZWZmZWN0IikNCg0KbGlicmFyeShnZ3JpZGdlcykNCg0KZ2dwbG90KGxvbmdwb3N0KSsNCiAgZ2VvbV9kZW5zaXR5KGFlcyh4PWVmZmVjdCxmaWxsPVZhcmlhYmxlKSxjb2w9ImJsYWNrIixzaG93LmxlZ2VuZCA9IEYsYWxwaGE9MC41KSsNCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUsbmNvbD0yLHNjYWxlcyA9ICJmcmVlIikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1wYWxzOjp0b2woOCkpK3RoZW1lX2J3KCkNCg0KZ2dwbG90KGxvbmdwb3N0KSsNCiAgZ2VvbV9wYXRoKGFlcyh5PWVmZmVjdCx4PUl0ZXJhdGlvbixjb2w9VmFyaWFibGUpLA0KICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLGFscGhhPTAuOCkrDQogIGZhY2V0X3dyYXAoflZhcmlhYmxlLG5jb2w9MixzY2FsZXMgPSAiZnJlZSIpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBhbHM6OnRvbCg4KSkrdGhlbWVfYncoKQ0KDQpgYGANCg0KUGjDom4gcGjhu5FpIGjhuq11IG5naGnhu4dtIGNobyBjw6FjIMSRaeG7gXUga2nhu4duL2hp4buHdSDhu6luZyBuw6B5IGhvw6BuIHRvw6BuIGjhu6NwIGzDvSwgdsOgIGPDsyB0aOG7gyBkaeG7hW4gZ2nhuqNpIG5oxrAgdGjGsOG7nW5nIGzhu4csIHRow60gZOG7pTogYuG7h25oIG5ow6JuIGLhu4sgdGh1ecOqbiB04bqvYyBwaOG7lWkgY8OzIGRkaW1lciB0xINuZyB0cnVuZyBiw6xuaCA2OCDEkcahbiB24buLLCBuZ3V5IGPGoSB04butIHZvbmcgY2FvIMSR4bq/biAyNDAgbOG6p24uIFZp4buHYyDEkWnhu4F1IHRy4buLIGzDoG0gZ2nhuqNtIG5ndXkgY8ahIHThu60gdm9uZyB24bubaSBPZGRzLXJhdGlvID0gMC4xNCwgLi4uDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbG9uZ3Bvc3QlPiVncm91cF9ieShWYXJpYWJsZSklPiUNCiAgc3VtbWFyaXNlX2F0KCJlZmZlY3QiLGZ1bnMoTWVkaWFuPW1lZGlhbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNEPXNkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgTEw9cXVhbnRpbGUoLixwcm9icz0wLjAyNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBVTD1xdWFudGlsZSguLHByb2JzPTAuOTc1KSkpDQpgYGANCg0KVHLGsOG7m2Mga2hpIGvhur90IHRow7pjLCBOaGkgbMOgbSB0aMOqbSBt4buZdCB0aOG7pyB0aHXhuq10IG5o4buPIG7hu69hICwgxJHhu4MgdHLDrG5oIGLDoHkgbuG7mWkgZHVuZyBtw7QgaMOsbmggbeG6oW5nIGTGsOG7m2kgZOG6oW5nIG3hu5l0IHPGoSDEkeG7kyBEQUcgY8OzIGvDqG0gT2Rkcy1yYXRpbyB2w6AgaGnhu4d1IOG7qW5nIGPhu6dhIG3hu5dpIG3DtCBow6xuaDoNCg0KYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRhZ19tYXQ8LW5ldCRkYWcNCg0KbGlicmFyeShnZ3JhcGgpDQoNCmNnPWRhdGEuZnJhbWUocm93PXJvd25hbWVzKGRhZ19tYXQpW3JvdyhkYWdfbWF0KV0sIA0KICAgICAgICAgICAgICBjb2w9Y29sbmFtZXMoZGFnX21hdClbY29sKGRhZ19tYXQpXSwgDQogICAgICAgICAgICAgIGxpbmtlZD1kYWdfbWF0JT4lYXMudmVjdG9yKCkpDQoNCm5hbWVzKGNnKT1jKCJmcm9tIiwidG8iLCJsaW5rZWQiKQ0KDQpjZ3NpbXBsZTwtZmlsdGVyKGNnLGxpbmtlZD09MSklPiUNCiAgbXV0YXRlKGVmZmVjdD1jKCJPUjoxLjc4IiwNCiAgICAgICAgICAgICAgICAgICJiOis2OC40IiwNCiAgICAgICAgICAgICAgICAgICJiOisyOS4yIiwNCiAgICAgICAgICAgICAgICAgICJPUjoyNi44IiwNCiAgICAgICAgICAgICAgICAgICJPUjoxLjAyIiwNCiAgICAgICAgICAgICAgICAgICJPUjo1LjY4IiwNCiAgICAgICAgICAgICAgICAgICJPUjoyNDEiLA0KICAgICAgICAgICAgICAgICAgIk9SOjAuMTQzIikpDQoNCmxpYnJhcnkoaWdyYXBoKQ0KDQpncmFwaDwtZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGNnc2ltcGxlKQ0KDQpnZ3JhcGgoZ3JhcGgsY2lyY3VsYXI9RikrDQogIGdlb21fZWRnZV9saW5rKGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCg2LCdtbScpKSwNCiAgICAgICAgICAgICAgICAgY29sb3VyPSJyZWQiLA0KICAgICAgICAgICAgICAgICBhZXMobGFiZWw9ZWZmZWN0KSkrDQogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBuYW1lKSkrDQogIGNvb3JkX2ZpeGVkKCkrDQogIHRoZW1lX2dyYXBoKCkNCiAgICAgICAgICAgICAgIA0KYGBgDQoNCiFbXShicm1zbmV0LnBuZykNCg0KDQojIEvhur90IGx14bqtbg0KDQpCw6BpIHRo4buxYyBow6BuaCDEkeG6v24gxJHDonkgbMOgIGjhur90LCBjaMO6bmcgdGEgduG7q2EgdGjhu60gbmdoaeG7h20gdMOtbmggbsSDbmcgbcO0IGjDrG5oIGPhuqV1IHRyw7pjIMSRYSBiaeG6v24gY+G7p2EgcGFja2FnZSBicm1zLiBUw61uaCBuxINuZyBuw6B5IGtow6EgbOG7o2kgaOG6oWksIHbDrCBuw7MgY2hvIHBow6lwIGNow7puZyB0YSBsYWkgZ2jDqXAgZ2nhu69hIHRo4buRbmcga8OqIEJheWVzIHbDoCBuaOG7r25nIHBoxrDGoW5nIHBow6FwIG3DtCBow6xuaCBj4bqldSB0csO6YyBraMOhYyBuaMawIFNFTSwgREFHLCBMYXRlbnQgdmFyLiBhbmFseXNpcywgbmV0d29yayAuLi4gduG7m2kgdMOtbmggbGluaCBob+G6oXQgcuG6pXQgbOG7m24sIHRo4bqtbSBjaMOtIGjGoW4gY+G6oyBuaOG7r25nIHBhY2thZ2Ugc+G7rSBk4bulbmcgSkFHcyBzYW1wbGVyLiANCg0KVuG7gSBnacOhIHRy4buLIHRo4buxYyB0aeG7hW4sIHBoxrDGoW5nIHBow6FwIG3huqFuZyBtw7QgaMOsbmggQmF5ZXMgKEJheWVzaWFuIG5ldHdvcmspbeG7nyByYSBuaGnhu4F1IOG7qW5nIGThu6VuZyB0aeG7gW0gbsSDbmcgY2hvIG5naGnDqm4gY+G7qXUgWSBo4buNYyBsw6JtIHPDoG5nLCB2w6wgY2hvIHBow6lwIGPDoWMgYuG6oW4ga+G7gyBt4buZdCBjw6J1IGNodXnhu4duIGjhuqVwIGThuqtuIHbDoCBo4bujcCBsw70gduG7m2kgZOG7ryBsaeG7h3UgaGnhu4duIGPDsywgc3V5IGRp4buFbiBuaMOibiBxdeG6oywgYmnhu4duIGx14bqtbiBxdXkgdHLDrG5oIMSRxrBhIHJhIHF1eeG6v3QgxJHhu4tuaCBjaOG6qW4gxJFvw6FuLCBwaMOhYyDEkeG7kyDEkWnhu4F1IHRy4buLLi4uYuG6sW5nIGvhur90IHF14bqjIHBow6JuIHTDrWNoIMSRYSBiaeG6v24sZOG7sWEgdsOgbyBsw70gdGh1eeG6v3QgeSBo4buNYyB2w6AgdOG6pXQgY+G6oyBuaOG7r25nIMawdSB0aOG6vyBtw6AgdHLGsOG7nW5nIHBow6FpIEJheWVzIGPDsyB0aOG7gyBtYW5nIGzhuqFpLg0KDQpDaMO6YyBjw6FjIGLhuqFuIHRow6BuaCBjw7RuZy4NCg==