1 Đặt vấn đề

Trong một số hoàn cảnh, đại lượng sinh lý/bệnh học cần khảo sát trong nghiên cứu được ghi nhận/theo dõi liên tục trong suốt một khoảng thời gian dài bằng một dụng cụ/máy móc tự động nào đó. Thí dụ:

  • Theo dõi sinh hiệu : SaO2, huyết áp, nhịp tim … của bệnh nhân trong phẫu thuật hoặc tại phòng cấp cứu.

  • Đếm tần suất cơn ho, số lần sử dụng thuốc dãn phế quản dạng hít của bệnh nhân mắc bệnh lý hô hấp

  • Theo dõi cơn gò tử cung ở sản phụ khi chờ chuyển dạ

  • Theo dõi biến cố về nhịp tim, độ bão hòa oxy máu trong giấc ngủ tại nhà

Lúc này, dữ liệu thô mà nghiên cứu sinh có trong tay là một chuỗi thời gian. Việc khai thác được thông tin từ dữ liệu dạng này không phải là điều dễ dàng; các bạn cần sự trợ giúp của chuyên viên thống kê để có thể chuyển từ dữ liệu chuỗi thành thông số lâm sàng phù hợp với mục tiêu nghiên cứu.

Trong bài hôm nay, Nhi sẽ trình bày một ý tưởng cho phép các bạn hoán chuyển từ dữ liệu chuỗi thành những thông số lâm sàng có ý nghĩa, từ đó cho phép thực hiện những mô hình và kiểm định thống kê.

2 Thí dụ minh họa

Thí dụ minh họa trong bài như sau:

Một bệnh nhân mắc hội chứng ngưng thở khi ngủ được theo dõi độ bão hòa oxy máu ngoại biên trong khi ngủ (tần số lấy mẫu là 1 Hz, hay 1 giá trị SaO2 mỗi giây) vào đêm trước và trong khi sử dụng một máy thở Oxy áp lực dương. Câu hỏi nghiên cứu là liệu cách điều trị này có hiệu quả cải thiện sự bão hòa oxy máu của bệnh nhân trên hay không ?

Sau khi tải dữ liệu, ta sẽ có 1 dataframe 47595 hàng (tương đương 13.2 giờ), biến SaO2 là một chuỗi giá trị rời rạc, biến Cond là trạng thái Trước (Control) và trong khi điều trị (Treatment)

library(tidyverse)

df = read.csv("Saturation.csv", sep = ";")

head(df)
##      Cond Time SaO2
## 1 Control    1   96
## 2 Control    2   96
## 3 Control    3   96
## 4 Control    4   96
## 5 Control    5   96
## 6 Control    6   96

Việc trước tiên Nhi sẽ làm, đó là nhị phân hóa giá trị SaO2, bằng cách này Nhi tạo ra một Biến cố: Mất bão hòa oxy, với 2 trạng thái: Có (Y) khi SaO2 < 94 và Không (N) khi SaO2 >= 94

Như vậy, chúng ta sẽ tiếp cận bài toán bằng cả 2 loại dữ liệu: Định lượng (SaO2) và Định tính (biến cố Desaturation).

df$Desat = if_else(df$SaO2 < 94, "Y","N")

head(df)
##      Cond Time SaO2 Desat
## 1 Control    1   96     N
## 2 Control    2   96     N
## 3 Control    3   96     N
## 4 Control    4   96     N
## 5 Control    5   96     N
## 6 Control    6   96     N

Bây giờ, mời các bạn cùng Nhi quan sát diễn tiến của SaO2 trong thời giàn hơn 6h, trong 2 điều kiện khác nhau

df %>% ggplot(aes(x = Time/3600, y = SaO2, col = Cond))+
  geom_step(direction = "hv", alpha = 0.4)+
  geom_smooth(se = F)+
  geom_hline(yintercept = 94, linetype = 2)+
  scale_color_manual(values = c("red","blue"))+
  xlab('Duration (h)')+
  theme_minimal()+
  facet_wrap(~Cond,ncol = 1)

Qua hình ảnh này, ta có cảm giác rằng khi điều trị bằng máy thở oxy, SaO2 trung bình có vẻ cao hơn; tuy nhiên làm cách nào để mô tả và kiểm tra điều này bằng con số và thống kê ?

Ta không thể dùng giá trị SaO2 ở thời gian thực trong cả 1 đêm làm biến số để so sánh, như hình vẽ bên dưới cho thấy: rất khó phân biệt 2 histogram phân bố của chuỗi SaO2

df %>% ggplot(aes(x = SaO2, fill = Cond))+
  geom_histogram(alpha = 0.5, binwidth = 1, col = "black")+
  geom_vline(xintercept = 94, linetype = 2)+
  scale_fill_manual(values = c("red","blue"))+
  facet_wrap(~Cond,ncol = 1)+
  theme_minimal()

Ta cũng không thể sử dụng giá trị trung vị hay trung bình làm đại diện cho chuỗi SaO2, như biểu đồ sau đây cho thấy không có sự khác biệt về vị trí trung tâm (cũng như hầu hết bách phân vị) giữa 2 chuỗi SaO2

library(lvplot)

df %>% ggplot(aes(x = Cond, y = SaO2, fill = Cond))+
  geom_lv(aes(alpha = ..LV..), col = "black", show.legend = F)+
  geom_hline(yintercept = 94, linetype = 2)+
  coord_flip()+
  scale_fill_manual(values = c("red","blue"))+
  theme_minimal()

3 Phép đếm tần suất và tái chọn mẫu

Các bạn còn nhớ ở trên ta đã tạo ra 1 chuỗi nhị phân là biến cố Desaturation, bây giờ các bạn thử tưởng tượng: Nếu ta đặt ra một thông số lâm sàng có tên là: Tần suất phát sinh biến cố mất bão hòa Oxy trong 1 giờ, hay số cơn giảm SpO2 trong 1h, đây có thể là một thông số có ý nghĩa đại diện và thực dụng.

Nhưng làm sao xác định được thông số này ? Cách làm đơn giản nhất, đó là ta đếm tổng số cơn mất bão hòa oxy trong cả đêm, rồi chia cho tổng thời gian, thí dụ ta đếm được 100 cơn, và thời gian ngủ là 6.5 giờ, ta sẽ có chỉ số = 15.38 cơn/h

Cách làm này đơn giản, nhưng có nhiều vấn đề như:

  • Không xét đến phân bố của biến cố theo thời gian : các biến cố có thể rải rác trong 6h, hoặc chỉ tập trung trong một số giai đoạn nhất định

  • Không xét đến thời gian kéo dài của mỗi biến cố: 5-10 biến cố kéo dài vài phút hẳn là nguy hiểm hơn 20-30 biến cố ngắn.

Do đó, Nhi sẽ viết một function với công dụng : Ước tính tần suất phát sinh biến cố Desaturation trong mỗi giờ và độ dài chuẩn hóa của desaturation trong 1h đó dựa vào chọn mẫu ngẫu nhiên.

Như vậy, Nhi xem chuỗi nhị phân Desaturation với hàng ngàn thời điểm là một không gian chọn mẫu, rồi chọn ngẫu nhiên một vị trí bất kì trên chuỗi này, đi đến phía trước 3599 giây; đếm tất cả những biến cố Desaturation = Y trong khoảng thời gian 1h này, tính tổng độ dài của chúng. Lặp lại hàng ngàn lần quy trình này, ta sẽ có thể mô tả được phân bố của 2 đại lượng mà ta cần khảo sát là tần suất phát sinh biến cố mỗi giờ, và độ dài của biến cố.

boot_count = function(org = NULL,replace = T,size = 10, samp_window = 3600){
  
  sample_idx = sample(c(1:(nrow(org)- samp_window - 1)), 
                      replace = replace, 
                      size = size)
  
  out_df = data.frame(Event = NA,
                      Duration = NA,
                      Norm_dt = NA,
                      Freq = NA,
                      Loc = NA)
  
  i = 1
  
  while (i <= length(sample_idx)){
    
    samp_df = org[c(sample_idx[i] : (sample_idx[i]+samp_window - 1)),]
    
    change.idx <- with(samp_df,which(head(Desat, -1) != tail(Desat, -1)))
    
    if(length(change.idx) > 0){
      
      events = samp_df[c(change.idx,max(change.idx)+1),]%>%.$Desat
      
      locs = samp_df[c(change.idx,max(change.idx)+1),]%>%.$Time
      
      start = c(1, change.idx+1)
      
      end = c(change.idx, nrow(samp_df))
      
      duration = 1 + end - start
      
      temp_df = data.frame('Event'= events,
                          'Duration' = duration)
      
      temp_df%>%group_by(Event)%>%
        summarise_at("Duration", sum)%>%
        mutate(Norm_dt = Duration/sum(Duration),
               Freq = as.vector(table(temp_df$Event)),
               Loc = mean(locs))-> sum_df
    }
    
    else{sum_df = data.frame(Event = samp_df[1,]%>%.$Desat,
                             Duration = samp_window,
                             Norm_dt = 1,
                             Freq = 1,
                             Loc = mean(samp_df$Time))}
    
    out_df = bind_rows(out_df, sum_df)
    
    i = i+1
    }
    
  return(out_df %>%na.omit())}

Nhi áp dụng quy trình nói trên 10 ngàn lần cho 2 chuỗi dữ liệu Control và Treatment, kết quả xuất ra như sau:

df %>% filter(Cond == "Control") %>% 
  boot_count(org = .,
             replace = F,
             size = 10000,
             samp_window = 3600)%>%
  mutate(Cond = "Baseline") -> out_base

head(out_base)
##   Event Duration    Norm_dt Freq       Loc     Cond
## 1     N     3425 0.95138889   35  9025.029 Baseline
## 2     Y      175 0.04861111   34  9025.029 Baseline
## 3     N     3426 0.95166667   15 20779.828 Baseline
## 4     Y      174 0.04833333   14 20779.828 Baseline
## 5     N     2494 0.69277778  114 15749.982 Baseline
## 6     Y     1106 0.30722222  114 15749.982 Baseline
df %>% filter(Cond != "Control") %>% 
  boot_count(org = .,
             replace = F,
             size = 10000,
             samp_window = 3600)%>%
  mutate(Cond = "Treatment") -> out_treat

head(out_treat)
##   Event Duration     Norm_dt Freq      Loc      Cond
## 1     N     3580 0.994444444    4 17028.14 Treatment
## 2     Y       20 0.005555556    3 17028.14 Treatment
## 3     N     3600 1.000000000    1 19645.50 Treatment
## 4     N     3470 0.963888889   10 14986.35 Treatment
## 5     Y      130 0.036111111   10 14986.35 Treatment
## 6     N     3148 0.874444444   46  6859.12 Treatment

Lúc này, ta đã có trong tay 2 bộ dữ liệu tái chọn mẫu cho 2 thông số : tần suất phát sinh Desaturation trong 1h, và độ dài của desaturation trong 1h. Với vị trí của cửa sổ chọn mẫu trên trục thời gian, ta có thể mô tả diễn tiến 2 thông số này:

Theo đó, ban đầu bệnh nhân dường như có số cơn Desaturation tương đương hoặc thậm chí cao hơn, nhưng sau khi ngủ 2h thì điều trị bắt đầu cho thấy hiệu quả: số cơn desaturation giảm về 0 trong suốt 2h, có tăng nhẹ lúc 4-5h nhưng sau đó =0 cho đến sáng.

comb_df = bind_rows(out_base, out_treat)

comb_df %>% filter(Event == 'Y')%>% 
  ggplot(aes(x = Loc/3600, y = Freq, col = Cond)) +
  geom_step()+
  xlab("Timeline (h)")+
  ylab('Hourly occuring rate (event/h)')+
  theme_bw()

Trong khi đó, tỉ lệ thời gian Desaturation (Đỏ) so với Bình thường (Xanh) cũng giảm đáng kể khi điều trị

out_base %>% 
  ggplot(aes(x = Loc/3600, 
             y = Norm_dt, 
             fill = Event)) +
  geom_bar(stat = "identity", 
           position = "fill")+
  scale_fill_manual(values = c("green","red"))+
  xlab("Duration (h)")+
  theme_bw()

out_treat %>% 
  ggplot(aes(x = Loc/3600, 
             y = Norm_dt, 
             fill = Event)) +
  geom_bar(stat = "identity", 
           position = "fill")+
  scale_fill_manual(values = c("green","red"))+
  xlab("Duration (h)")+
  theme_bw()

Bản thân thời gian chuẩn hóa của Desaturation cũng theo diễn tiến tương tự như chỉ số tần suất phát sinh biến cố vậy:

comb_df %>% filter(Event == 'Y')%>% 
  ggplot(aes(x = Loc/3600, y = Norm_dt, col = Cond)) +
  geom_step()+ 
  xlab("Timeline (h)")+
  ylab('Normalized event duration')+
  theme_bw()

Ưu điểm của quy trình tái chọn mẫu ngẫu nhiên, đó là nó cho phép mô tả đặc tính phân bố của 2 thông số lâm sàng này, và qua so sánh hình ảnh 2 phân bố này, ta thấy được sự tương phản giữa Trước và Trong khi điều trị:

Theo đó, điều trị bằng máy CPAP làm giảm đáng kể tần suất phát sinh biến cố Desaturation / 1h:

comb_df %>% filter(Event == 'Y')%>% 
  ggplot(aes(x = Freq)) +
  geom_histogram(alpha = 0.5, aes(fill = Cond), col = "black", binwidth = 5)+
  facet_wrap(~Cond, ncol = 1)+
  xlab('Hourly occuring rate (event/h)')+
  scale_fill_manual(values = c("red","blue"))+
  theme_bw()

Nó cũng làm giảm tổng thời gian Desaturation / mỗi giờ

comb_df %>% filter(Event == 'Y')%>% 
  ggplot(aes(x = Norm_dt)) +
  geom_density(alpha = 0.5, aes(fill = Cond), col = "black", binwidth = 5)+
  xlab("Normalized duration")+
  scale_fill_manual(values = c("red","blue"))+
  theme_bw()

Ta có thể thực hiện suy diễn thống kê cho dữ liệu tái chọn mẫu bằng mô hình, thí dụ mô hình sau đây đánh giá hiệu ứng điều trị lên tần suất phát sinh desaturation mỗi giờ - được khảo sát như biến số đếm; mô hình dựa vào phân bố Negative Binomial (Nhị thức âm) có xét thêm Spline function cho thời gian.

Dựa vào suy diễn qua Incidence rate ratio, có thể nói điều trị làm giảm khoảng 50% nguy cơ phát sinh Desaturation mỗi giờ:

library(gamlss)

mod = gamlss(data = comb_df[comb_df$Event == "Y",], 
             formula = Freq ~ Cond + bs(Loc),
             family = "NBII")
## GAMLSS-RS iteration 1: Global Deviance = 178120 
## GAMLSS-RS iteration 2: Global Deviance = 178101.5 
## GAMLSS-RS iteration 3: Global Deviance = 178100.6 
## GAMLSS-RS iteration 4: Global Deviance = 178100.5 
## GAMLSS-RS iteration 5: Global Deviance = 178100.5 
## GAMLSS-RS iteration 6: Global Deviance = 178100.5
summary(mod)
## ******************************************************************
## Family:  c("NBII", "Negative Binomial type II") 
## 
## Call:  
## gamlss(formula = Freq ~ Cond + bs(Loc), family = "NBII", data = comb_df[comb_df$Event ==  
##     "Y", ]) 
## 
## Fitting method: RS() 
## 
## ------------------------------------------------------------------
## Mu link function:  log
## Mu Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    4.172536   0.021760  191.75   <2e-16 ***
## CondTreatment -0.714591   0.009304  -76.81   <2e-16 ***
## bs(Loc)1      -0.537974   0.057974   -9.28   <2e-16 ***
## bs(Loc)2       0.358312   0.027597   12.98   <2e-16 ***
## bs(Loc)3      -0.498868   0.028602  -17.44   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Sigma link function:  log
## Sigma Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.90988    0.01084   268.6   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## No. of observations in the fit:  19673 
## Degrees of Freedom for the fit:  6
##       Residual Deg. of Freedom:  19667 
##                       at cycle:  6 
##  
## Global Deviance:     178100.5 
##             AIC:     178112.5 
##             SBC:     178159.8 
## ******************************************************************

Tương tự, ta có thể dùng mô hình với phân bố Beta cho thời gian desaturation /h chuẩn hóa (dao động từ 0 đến 1). Mô hình cho thấy điều trị làm giảm 0.46 lần thời gian Desaturation / h

mod = gamlss(data = comb_df[comb_df$Event == "Y",], 
             formula = Norm_dt ~ Cond + bs(Loc),
             family = "BE")
## GAMLSS-RS iteration 1: Global Deviance = -48227.25 
## GAMLSS-RS iteration 2: Global Deviance = -53240.21 
## GAMLSS-RS iteration 3: Global Deviance = -54749.65 
## GAMLSS-RS iteration 4: Global Deviance = -54993.09 
## GAMLSS-RS iteration 5: Global Deviance = -55018.68 
## GAMLSS-RS iteration 6: Global Deviance = -55020.91 
## GAMLSS-RS iteration 7: Global Deviance = -55021.09 
## GAMLSS-RS iteration 8: Global Deviance = -55021.11 
## GAMLSS-RS iteration 9: Global Deviance = -55021.11 
## GAMLSS-RS iteration 10: Global Deviance = -55021.11
summary(mod)
## ******************************************************************
## Family:  c("BE", "Beta") 
## 
## Call:  
## gamlss(formula = Norm_dt ~ Cond + bs(Loc), family = "BE", data = comb_df[comb_df$Event ==  
##     "Y", ]) 
## 
## Fitting method: RS() 
## 
## ------------------------------------------------------------------
## Mu link function:  logit
## Mu Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   -1.31864    0.02660 -49.581  < 2e-16 ***
## CondTreatment -0.72049    0.01131 -63.731  < 2e-16 ***
## bs(Loc)1      -1.73454    0.07146 -24.272  < 2e-16 ***
## bs(Loc)2       0.18401    0.03432   5.361 8.37e-08 ***
## bs(Loc)3      -0.53798    0.03406 -15.793  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Sigma link function:  logit
## Sigma Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -0.975601   0.005721  -170.5   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## No. of observations in the fit:  19673 
## Degrees of Freedom for the fit:  6
##       Residual Deg. of Freedom:  19667 
##                       at cycle:  10 
##  
## Global Deviance:     -55021.11 
##             AIC:     -55009.11 
##             SBC:     -54961.79 
## ******************************************************************

4 Trung bình và tái chọn mẫu

Ở trên, chúng ta tạo ra thông số lâm sàng dựa vào chuỗi giá trị định tính, bây giờ ta sẽ xét đến chuỗi định lượng, là chính bản thân SaO2.

Áp dụng cơ chế tương tự, Nhi sẽ tạo ra một chỉ số lâm sàng có tên là: SaO2 trung bình / 10 phút; các bạn còn nhớ mình đã ghi nhận sinh hiệu cho bệnh nhân bằng cách đo đếm thủ công trong 1 phút, ở đây ta cũng làm tương tự, khi đặt một cửa sổ chọn mẫu ngẫu nhiên dài 10 phút (600 quan sát) tại những vị trí bất kì trên chuỗi thời gian. Để làm việc này, Nhi viết thêm 1 hàm nữa như sau:

boot_mean = function(org = NULL,replace = T,size = 10, samp_window = 600){
  
  sample_idx = sample(c(1:(nrow(org)- samp_window - 1)), 
                      replace = replace, 
                      size = size)
  
  out_df = data.frame(Mean_SaO2 = rep(NA,size),Loc = rep(NA,size))
  
  i = 1
  
  while (i <= length(sample_idx)){
    samp_df = org[c(sample_idx[i] : (sample_idx[i]+samp_window - 1)),]
    
    out_df$Mean_SaO2[i] = mean(samp_df$SaO2)
    out_df$Loc[i] = mean(samp_df$Time)
    
    i = i + 1
  }
  
  return(na.omit(out_df))
}

Nhi lần lượt áp dụng hàm boot_mean này cho 2 chuỗi Control và Treatment, lưu ý tùy chỉnh replace = T cho thấy đây là 1 quy trình Bootstrap 10 ngàn lượt

df %>% filter(Cond != "Control") %>% 
  boot_mean(org = .,
             replace = T,
             size = 10000,
             samp_window = 600)%>%
  mutate(Cond = "Treatment") -> sa_tm

head(sa_tm)
##   Mean_SaO2     Loc      Cond
## 1  94.37667 15611.5 Treatment
## 2  94.57667  2218.5 Treatment
## 3  94.62500  8858.5 Treatment
## 4  96.07833 18331.5 Treatment
## 5  96.14000 20063.5 Treatment
## 6  95.29833 13773.5 Treatment
df %>% filter(Cond == "Control") %>% 
  boot_mean(org = .,
             replace = T,
             size = 10000,
             samp_window = 600)%>%
  mutate(Cond = "Control") -> sa_base

head(sa_base)
##   Mean_SaO2     Loc    Cond
## 1  93.09500 16690.5 Control
## 2  94.93500  3639.5 Control
## 3  95.32333 17744.5 Control
## 4  95.86833   598.5 Control
## 5  95.01500  2732.5 Control
## 6  93.92833 15188.5 Control

Tương tự, ta có thể khảo sát hình ảnh phân bố của chỉ số lâm sàng vừa tạo ra là SaO2 trung bình trong 10 phút (lưu ý là bạn có thể chọn kích thước cửa sổ này tùy ý, 1 phút, 5 phút, 10 phút, thậm chí 30 phút)

comb_sa = bind_rows(sa_base, sa_tm)

comb_sa %>% ggplot(aes(x = Mean_SaO2, fill = Cond))+
  geom_histogram(alpha = 0.5, col = "black")+
  xlab('SaO2 / 10 min')+
  theme_bw()

comb_sa %>%
  ggplot(aes(x = Cond, y = Mean_SaO2)) +
  geom_lv(alpha = 0.5, aes(fill = Cond), col = "black")+
  coord_flip()+
  theme_bw()

Ta cũng không mất thông tin về yếu tố thời gian, vì vị trí chọn mẫu được lưu lại trong kết quả, ta có thể khảo sát diễn tiến của SaO2/10ph

comb_sa %>% 
  ggplot(aes(x = Loc/3600, y = Mean_SaO2, col = Cond)) +
  geom_step()+ 
  xlab("Timeline (h)")+
  ylab('SaO2 / 10 min')+
  theme_bw()

5 Bàn luận

Thông điệp quan trọng nhất mà Nhi muốn chia sẻ với các bạn qua bài này, đó là kỹ thuật lập trình thống kê có tiềm năng vô cùng lớn. Nó không chỉ cho phép lập kế hoạch phân tích trên những bảng dữ liệu thông số lâm sàng có sẵn, mà còn cho phép tạo ra cả những đại lượng hoàn toàn mới từ dữ liệu thô. Cơ chế được sử dụng trong bài rất đơn giản, đó là tái chọn mẫu ngẫu nhiên hoặc bootstrap, với một ít kỹ thuật về vòng lặp trong R nhưng kết quả tạo ra là khá ấn tượng. Ghi chú thêm là thí dụ minh họa này chỉ mới là một chuỗi nhị phân, tuy nhiên kỹ thuật tái chọn mẫu có thể mở rộng cho chuỗi đa sự kiện (nhiều loại biến cố),

Chúc các bạn vững bước trên hành trình khoa học với R

LS0tDQp0aXRsZTogIlThuqFvIHRow7RuZyBz4buRIGzDom0gc8OgbmcgdOG7qyBk4buvIGxp4buHdSBjaHXhu5dpIHRo4budaSBnaWFuIiANCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjE3IFRow6FuZyAxMSBuxINtIDIwMTkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBkZXY6ICdzdmcnDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQohW10oYWxpY2UucG5nKQ0KDQojIMSQ4bq3dCB24bqlbiDEkeG7gQ0KDQoNClRyb25nIG3hu5l0IHPhu5EgaG/DoG4gY+G6o25oLCDEkeG6oWkgbMaw4bujbmcgc2luaCBsw70vYuG7h25oIGjhu41jIGPhuqduIGto4bqjbyBzw6F0IHRyb25nIG5naGnDqm4gY+G7qXUgxJHGsOG7o2MgZ2hpIG5o4bqtbi90aGVvIGTDtWkgbGnDqm4gdOG7pWMgdHJvbmcgc3Xhu5F0IG3hu5l0IGtob+G6o25nIHRo4budaSBnaWFuIGTDoGkgYuG6sW5nIG3hu5l0IGThu6VuZyBj4bulL23DoXkgbcOzYyB04buxIMSR4buZbmcgbsOgbyDEkcOzLiBUaMOtIGThu6U6DQoNCisgVGhlbyBkw7VpIHNpbmggaGnhu4d1IDogU2FPMiwgaHV54bq/dCDDoXAsIG5o4buLcCB0aW0gLi4uIGPhu6dhIGLhu4duaCBuaMOibiB0cm9uZyBwaOG6q3UgdGh14bqtdCBob+G6t2MgdOG6oWkgcGjDsm5nIGPhuqVwIGPhu6l1Lg0KDQorIMSQ4bq/bSB04bqnbiBzdeG6pXQgY8ahbiBobywgc+G7kSBs4bqnbiBz4butIGThu6VuZyB0aHXhu5FjIGTDo24gcGjhur8gcXXhuqNuIGThuqFuZyBow610IGPhu6dhIGLhu4duaCBuaMOibiBt4bqvYyBi4buHbmggbMO9IGjDtCBo4bqlcA0KDQorIFRoZW8gZMO1aSBjxqFuIGfDsiB04butIGN1bmcg4bufIHPhuqNuIHBo4bulIGtoaSBjaOG7nSBjaHV54buDbiBk4bqhDQoNCisgVGhlbyBkw7VpIGJp4bq/biBj4buRIHbhu4Egbmjhu4twIHRpbSwgxJHhu5kgYsOjbyBow7JhIG94eSBtw6F1IHRyb25nIGdp4bqlYyBuZ+G7pyB04bqhaSBuaMOgDQoNCkzDumMgbsOgeSwgZOG7ryBsaeG7h3UgdGjDtCBtw6AgbmdoacOqbiBj4bupdSBzaW5oIGPDsyB0cm9uZyB0YXkgbMOgIG3hu5l0IGNodeG7l2kgdGjhu51pIGdpYW4uIFZp4buHYyBraGFpIHRow6FjIMSRxrDhu6NjIHRow7RuZyB0aW4gdOG7qyBk4buvIGxp4buHdSBk4bqhbmcgbsOgeSBraMO0bmcgcGjhuqNpIGzDoCDEkWnhu4F1IGThu4UgZMOgbmc7IGPDoWMgYuG6oW4gY+G6p24gc+G7sSB0cuG7oyBnacO6cCBj4bunYSBjaHV5w6puIHZpw6puIHRo4buRbmcga8OqIMSR4buDIGPDsyB0aOG7gyBjaHV54buDbiB04burIGThu68gbGnhu4d1IGNodeG7l2kgdGjDoG5oIHRow7RuZyBz4buRIGzDom0gc8OgbmcgcGjDuSBo4bujcCB24bubaSBt4bulYyB0acOqdSBuZ2hpw6puIGPhu6l1Lg0KDQpUcm9uZyBiw6BpIGjDtG0gbmF5LCBOaGkgc+G6vSB0csOsbmggYsOgeSBt4buZdCDDvSB0xrDhu59uZyBjaG8gcGjDqXAgY8OhYyBi4bqhbiBob8OhbiBjaHV54buDbiB04burIGThu68gbGnhu4d1IGNodeG7l2kgdGjDoG5oIG5o4buvbmcgdGjDtG5nIHPhu5EgbMOibSBzw6BuZyBjw7Mgw70gbmdoxKlhLCB04burIMSRw7MgY2hvIHBow6lwIHRo4buxYyBoaeG7h24gbmjhu69uZyBtw7QgaMOsbmggdsOgIGtp4buDbSDEkeG7i25oIHRo4buRbmcga8OqLiANCg0KIyBUaMOtIGThu6UgbWluaCBo4buNYQ0KDQpUaMOtIGThu6UgbWluaCBo4buNYSB0cm9uZyBiw6BpIG5oxrAgc2F1OiANCg0KTeG7mXQgYuG7h25oIG5ow6JuIG3huq9jIGjhu5lpIGNo4bupbmcgbmfGsG5nIHRo4bufIGtoaSBuZ+G7pyDEkcaw4bujYyB0aGVvIGTDtWkgxJHhu5kgYsOjbyBow7JhIG94eSBtw6F1IG5nb+G6oWkgYmnDqm4gdHJvbmcga2hpIG5n4bunICh04bqnbiBz4buRIGzhuqV5IG3huqt1IGzDoCAxIEh6LCBoYXkgMSBnacOhIHRy4buLIFNhTzIgbeG7l2kgZ2nDonkpIHbDoG8gxJHDqm0gdHLGsOG7m2MgdsOgIHRyb25nIGtoaSBz4butIGThu6VuZyBt4buZdCBtw6F5IHRo4bufIE94eSDDoXAgbOG7sWMgZMawxqFuZy4gQ8OidSBo4buPaSBuZ2hpw6puIGPhu6l1IGzDoCBsaeG7h3UgY8OhY2ggxJFp4buBdSB0cuG7iyBuw6B5IGPDsyBoaeG7h3UgcXXhuqMgY+G6o2kgdGhp4buHbiBz4buxIGLDo28gaMOyYSBveHkgbcOhdSBj4bunYSBi4buHbmggbmjDom4gdHLDqm4gaGF5IGtow7RuZyA/DQoNClNhdSBraGkgdOG6o2kgZOG7ryBsaeG7h3UsIHRhIHPhur0gY8OzIDEgZGF0YWZyYW1lIDQ3NTk1IGjDoG5nICh0xrDGoW5nIMSRxrDGoW5nIDEzLjIgZ2nhu50pLCBiaeG6v24gU2FPMiBsw6AgbeG7mXQgY2h14buXaSBnacOhIHRy4buLIHLhu51pIHLhuqFjLCBiaeG6v24gQ29uZCBsw6AgdHLhuqFuZyB0aMOhaSBUcsaw4bubYyAoQ29udHJvbCkgdsOgIHRyb25nIGtoaSDEkWnhu4F1IHRy4buLIChUcmVhdG1lbnQpDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmRmID0gcmVhZC5jc3YoIlNhdHVyYXRpb24uY3N2Iiwgc2VwID0gIjsiKQ0KDQpoZWFkKGRmKQ0KYGBgDQoNClZp4buHYyB0csaw4bubYyB0acOqbiBOaGkgc+G6vSBsw6BtLCDEkcOzIGzDoCBuaOG7iyBwaMOibiBow7NhIGdpw6EgdHLhu4sgU2FPMiwgYuG6sW5nIGPDoWNoIG7DoHkgTmhpIHThuqFvIHJhIG3hu5l0IEJp4bq/biBj4buROiBN4bqldCBiw6NvIGjDsmEgb3h5LCB24bubaSAyIHRy4bqhbmcgdGjDoWk6IEPDsyAoWSkga2hpIFNhTzIgPCA5NCB2w6AgS2jDtG5nIChOKSBraGkgU2FPMiA+PSA5NA0KDQpOaMawIHbhuq15LCBjaMO6bmcgdGEgc+G6vSB0aeG6v3AgY+G6rW4gYsOgaSB0b8OhbiBi4bqxbmcgY+G6oyAyIGxv4bqhaSBk4buvIGxp4buHdTogxJDhu4tuaCBsxrDhu6NuZyAoU2FPMikgdsOgIMSQ4buLbmggdMOtbmggKGJp4bq/biBj4buRIERlc2F0dXJhdGlvbikuDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGYkRGVzYXQgPSBpZl9lbHNlKGRmJFNhTzIgPCA5NCwgIlkiLCJOIikNCg0KaGVhZChkZikNCmBgYA0KDQpCw6J5IGdp4budLCBt4budaSBjw6FjIGLhuqFuIGPDuW5nIE5oaSBxdWFuIHPDoXQgZGnhu4VuIHRp4bq/biBj4bunYSBTYU8yIHRyb25nIHRo4budaSBnacOgbiBoxqFuIDZoLCB0cm9uZyAyIMSRaeG7gXUga2nhu4duIGtow6FjIG5oYXUNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRmICU+JSBnZ3Bsb3QoYWVzKHggPSBUaW1lLzM2MDAsIHkgPSBTYU8yLCBjb2wgPSBDb25kKSkrDQogIGdlb21fc3RlcChkaXJlY3Rpb24gPSAiaHYiLCBhbHBoYSA9IDAuNCkrDQogIGdlb21fc21vb3RoKHNlID0gRikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDk0LCBsaW5ldHlwZSA9IDIpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwiYmx1ZSIpKSsNCiAgeGxhYignRHVyYXRpb24gKGgpJykrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgZmFjZXRfd3JhcCh+Q29uZCxuY29sID0gMSkNCg0KYGBgDQoNClF1YSBow6xuaCDhuqNuaCBuw6B5LCB0YSBjw7MgY+G6o20gZ2nDoWMgcuG6sW5nIGtoaSDEkWnhu4F1IHRy4buLIGLhurFuZyBtw6F5IHRo4bufIG94eSwgU2FPMiB0cnVuZyBiw6xuaCBjw7MgduG6uyBjYW8gaMahbjsgdHV5IG5oacOqbiBsw6BtIGPDoWNoIG7DoG8gxJHhu4MgbcO0IHThuqMgdsOgIGtp4buDbSB0cmEgxJFp4buBdSBuw6B5IGLhurFuZyBjb24gc+G7kSB2w6AgdGjhu5FuZyBrw6ogPw0KDQpUYSBraMO0bmcgdGjhu4MgZMO5bmcgZ2nDoSB0cuG7iyBTYU8yIOG7nyB0aOG7nWkgZ2lhbiB0aOG7sWMgdHJvbmcgY+G6oyAxIMSRw6ptIGzDoG0gYmnhur9uIHPhu5EgxJHhu4Mgc28gc8OhbmgsIG5oxrAgaMOsbmggduG6vSBiw6puIGTGsOG7m2kgY2hvIHRo4bqleTogcuG6pXQga2jDsyBwaMOibiBiaeG7h3QgMiBoaXN0b2dyYW0gcGjDom4gYuG7kSBj4bunYSBjaHXhu5dpIFNhTzINCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRmICU+JSBnZ3Bsb3QoYWVzKHggPSBTYU8yLCBmaWxsID0gQ29uZCkpKw0KICBnZW9tX2hpc3RvZ3JhbShhbHBoYSA9IDAuNSwgYmlud2lkdGggPSAxLCBjb2wgPSAiYmxhY2siKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gOTQsIGxpbmV0eXBlID0gMikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsImJsdWUiKSkrDQogIGZhY2V0X3dyYXAofkNvbmQsbmNvbCA9IDEpKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNClRhIGPFqW5nIGtow7RuZyB0aOG7gyBz4butIGThu6VuZyBnacOhIHRy4buLIHRydW5nIHbhu4sgaGF5IHRydW5nIGLDrG5oIGzDoG0gxJHhuqFpIGRp4buHbiBjaG8gY2h14buXaSBTYU8yLCBuaMawIGJp4buDdSDEkeG7kyBzYXUgxJHDonkgY2hvIHRo4bqleSBraMO0bmcgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IHbhu4EgduG7iyB0csOtIHRydW5nIHTDom0gKGPFqW5nIG5oxrAgaOG6p3UgaOG6v3QgYsOhY2ggcGjDom4gduG7iykgZ2nhu69hIDIgY2h14buXaSBTYU8yDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShsdnBsb3QpDQoNCmRmICU+JSBnZ3Bsb3QoYWVzKHggPSBDb25kLCB5ID0gU2FPMiwgZmlsbCA9IENvbmQpKSsNCiAgZ2VvbV9sdihhZXMoYWxwaGEgPSAuLkxWLi4pLCBjb2wgPSAiYmxhY2siLCBzaG93LmxlZ2VuZCA9IEYpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA5NCwgbGluZXR5cGUgPSAyKSsNCiAgY29vcmRfZmxpcCgpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIikpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMgUGjDqXAgxJHhur9tIHThuqduIHN14bqldCB2w6AgdMOhaSBjaOG7jW4gbeG6q3UNCg0KQ8OhYyBi4bqhbiBjw7JuIG5o4bubIOG7nyB0csOqbiB0YSDEkcOjIHThuqFvIHJhIDEgY2h14buXaSBuaOG7iyBwaMOibiBsw6AgYmnhur9uIGPhu5EgRGVzYXR1cmF0aW9uLCBiw6J5IGdp4budIGPDoWMgYuG6oW4gdGjhu60gdMaw4bufbmcgdMaw4bujbmc6IE7hur91IHRhIMSR4bq3dCByYSBt4buZdCB0aMO0bmcgc+G7kSBsw6JtIHPDoG5nIGPDsyB0w6puIGzDoDogVOG6p24gc3XhuqV0IHBow6F0IHNpbmggYmnhur9uIGPhu5EgbeG6pXQgYsOjbyBow7JhIE94eSB0cm9uZyAxIGdp4budLCBoYXkgc+G7kSBjxqFuIGdp4bqjbSBTcE8yIHRyb25nIDFoLCDEkcOieSBjw7MgdGjhu4MgbMOgIG3hu5l0IHRow7RuZyBz4buRIGPDsyDDvSBuZ2jEqWEgxJHhuqFpIGRp4buHbiB2w6AgdGjhu7FjIGThu6VuZy4NCg0KTmjGsG5nIGzDoG0gc2FvIHjDoWMgxJHhu4tuaCDEkcaw4bujYyB0aMO0bmcgc+G7kSBuw6B5ID8gQ8OhY2ggbMOgbSDEkcahbiBnaeG6o24gbmjhuqV0LCDEkcOzIGzDoCB0YSDEkeG6v20gdOG7lW5nIHPhu5EgY8ahbiBt4bqldCBiw6NvIGjDsmEgb3h5IHRyb25nIGPhuqMgxJHDqm0sIHLhu5NpIGNoaWEgY2hvIHThu5VuZyB0aOG7nWkgZ2lhbiwgdGjDrSBk4bulIHRhIMSR4bq/bSDEkcaw4bujYyAxMDAgY8ahbiwgdsOgIHRo4budaSBnaWFuIG5n4bunIGzDoCA2LjUgZ2nhu50sIHRhIHPhur0gY8OzIGNo4buJIHPhu5EgPSAxNS4zOCBjxqFuL2gNCg0KQ8OhY2ggbMOgbSBuw6B5IMSRxqFuIGdp4bqjbiwgbmjGsG5nIGPDsyBuaGnhu4F1IHbhuqVuIMSR4buBIG5oxrA6DQoNCisgS2jDtG5nIHjDqXQgxJHhur9uIHBow6JuIGLhu5EgY+G7p2EgYmnhur9uIGPhu5EgdGhlbyB0aOG7nWkgZ2lhbiA6IGPDoWMgYmnhur9uIGPhu5EgY8OzIHRo4buDIHLhuqNpIHLDoWMgdHJvbmcgNmgsIGhv4bq3YyBjaOG7iSB04bqtcCB0cnVuZyB0cm9uZyBt4buZdCBz4buRIGdpYWkgxJFv4bqhbiBuaOG6pXQgxJHhu4tuaA0KDQorIEtow7RuZyB4w6l0IMSR4bq/biB0aOG7nWkgZ2lhbiBrw6lvIGTDoGkgY+G7p2EgbeG7l2kgYmnhur9uIGPhu5E6IDUtMTAgYmnhur9uIGPhu5Ega8OpbyBkw6BpIHbDoGkgcGjDunQgaOG6s24gbMOgIG5ndXkgaGnhu4NtIGjGoW4gMjAtMzAgYmnhur9uIGPhu5Egbmfhuq9uLg0KDQpEbyDEkcOzLCBOaGkgc+G6vSB2aeG6v3QgbeG7mXQgZnVuY3Rpb24gduG7m2kgY8O0bmcgZOG7pW5nIDogxq/hu5tjIHTDrW5oIHThuqduIHN14bqldCBwaMOhdCBzaW5oIGJp4bq/biBj4buRIERlc2F0dXJhdGlvbiB0cm9uZyBt4buXaSBnaeG7nSB2w6AgxJHhu5kgZMOgaSBjaHXhuqluIGjDs2EgY+G7p2EgZGVzYXR1cmF0aW9uIHRyb25nIDFoIMSRw7MgZOG7sWEgdsOgbyBjaOG7jW4gbeG6q3Ugbmfhuqt1IG5oacOqbi4NCg0KTmjGsCB24bqteSwgTmhpIHhlbSBjaHXhu5dpIG5o4buLIHBow6JuIERlc2F0dXJhdGlvbiB24bubaSBow6BuZyBuZ8OgbiB0aOG7nWkgxJFp4buDbSBsw6AgbeG7mXQga2jDtG5nIGdpYW4gY2jhu41uIG3huqt1LCBy4buTaSBjaOG7jW4gbmfhuqt1IG5oacOqbiBt4buZdCB24buLIHRyw60gYuG6pXQga8OsIHRyw6puIGNodeG7l2kgbsOgeSwgxJFpIMSR4bq/biBwaMOtYSB0csaw4bubYyAzNTk5IGdpw6J5OyDEkeG6v20gdOG6pXQgY+G6oyBuaOG7r25nIGJp4bq/biBj4buRIERlc2F0dXJhdGlvbiA9IFkgdHJvbmcga2hv4bqjbmcgdGjhu51pIGdpYW4gMWggbsOgeSwgdMOtbmggdOG7lW5nIMSR4buZIGTDoGkgY+G7p2EgY2jDum5nLiBM4bq3cCBs4bqhaSBow6BuZyBuZ8OgbiBs4bqnbiBxdXkgdHLDrG5oIG7DoHksIHRhIHPhur0gY8OzIHRo4buDIG3DtCB04bqjIMSRxrDhu6NjIHBow6JuIGLhu5EgY+G7p2EgMiDEkeG6oWkgbMaw4bujbmcgbcOgIHRhIGPhuqduIGto4bqjbyBzw6F0IGzDoCB04bqnbiBzdeG6pXQgcGjDoXQgc2luaCBiaeG6v24gY+G7kSBt4buXaSBnaeG7nSwgdsOgIMSR4buZIGTDoGkgY+G7p2EgYmnhur9uIGPhu5EuDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KYm9vdF9jb3VudCA9IGZ1bmN0aW9uKG9yZyA9IE5VTEwscmVwbGFjZSA9IFQsc2l6ZSA9IDEwLCBzYW1wX3dpbmRvdyA9IDM2MDApew0KICANCiAgc2FtcGxlX2lkeCA9IHNhbXBsZShjKDE6KG5yb3cob3JnKS0gc2FtcF93aW5kb3cgLSAxKSksIA0KICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSByZXBsYWNlLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gc2l6ZSkNCiAgDQogIG91dF9kZiA9IGRhdGEuZnJhbWUoRXZlbnQgPSBOQSwNCiAgICAgICAgICAgICAgICAgICAgICBEdXJhdGlvbiA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgIE5vcm1fZHQgPSBOQSwNCiAgICAgICAgICAgICAgICAgICAgICBGcmVxID0gTkEsDQogICAgICAgICAgICAgICAgICAgICAgTG9jID0gTkEpDQogIA0KICBpID0gMQ0KICANCiAgd2hpbGUgKGkgPD0gbGVuZ3RoKHNhbXBsZV9pZHgpKXsNCiAgICANCiAgICBzYW1wX2RmID0gb3JnW2Moc2FtcGxlX2lkeFtpXSA6IChzYW1wbGVfaWR4W2ldK3NhbXBfd2luZG93IC0gMSkpLF0NCiAgICANCiAgICBjaGFuZ2UuaWR4IDwtIHdpdGgoc2FtcF9kZix3aGljaChoZWFkKERlc2F0LCAtMSkgIT0gdGFpbChEZXNhdCwgLTEpKSkNCiAgICANCiAgICBpZihsZW5ndGgoY2hhbmdlLmlkeCkgPiAwKXsNCiAgICAgIA0KICAgICAgZXZlbnRzID0gc2FtcF9kZltjKGNoYW5nZS5pZHgsbWF4KGNoYW5nZS5pZHgpKzEpLF0lPiUuJERlc2F0DQogICAgICANCiAgICAgIGxvY3MgPSBzYW1wX2RmW2MoY2hhbmdlLmlkeCxtYXgoY2hhbmdlLmlkeCkrMSksXSU+JS4kVGltZQ0KICAgICAgDQogICAgICBzdGFydCA9IGMoMSwgY2hhbmdlLmlkeCsxKQ0KICAgICAgDQogICAgICBlbmQgPSBjKGNoYW5nZS5pZHgsIG5yb3coc2FtcF9kZikpDQogICAgICANCiAgICAgIGR1cmF0aW9uID0gMSArIGVuZCAtIHN0YXJ0DQogICAgICANCiAgICAgIHRlbXBfZGYgPSBkYXRhLmZyYW1lKCdFdmVudCc9IGV2ZW50cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ0R1cmF0aW9uJyA9IGR1cmF0aW9uKQ0KICAgICAgDQogICAgICB0ZW1wX2RmJT4lZ3JvdXBfYnkoRXZlbnQpJT4lDQogICAgICAgIHN1bW1hcmlzZV9hdCgiRHVyYXRpb24iLCBzdW0pJT4lDQogICAgICAgIG11dGF0ZShOb3JtX2R0ID0gRHVyYXRpb24vc3VtKER1cmF0aW9uKSwNCiAgICAgICAgICAgICAgIEZyZXEgPSBhcy52ZWN0b3IodGFibGUodGVtcF9kZiRFdmVudCkpLA0KICAgICAgICAgICAgICAgTG9jID0gbWVhbihsb2NzKSktPiBzdW1fZGYNCiAgICB9DQogICAgDQogICAgZWxzZXtzdW1fZGYgPSBkYXRhLmZyYW1lKEV2ZW50ID0gc2FtcF9kZlsxLF0lPiUuJERlc2F0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEdXJhdGlvbiA9IHNhbXBfd2luZG93LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOb3JtX2R0ID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRnJlcSA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvYyA9IG1lYW4oc2FtcF9kZiRUaW1lKSl9DQogICAgDQogICAgb3V0X2RmID0gYmluZF9yb3dzKG91dF9kZiwgc3VtX2RmKQ0KICAgIA0KICAgIGkgPSBpKzENCiAgICB9DQogICAgDQogIHJldHVybihvdXRfZGYgJT4lbmEub21pdCgpKX0NCmBgYA0KDQpOaGkgw6FwIGThu6VuZyBxdXkgdHLDrG5oIG7Ds2kgdHLDqm4gMTAgbmfDoG4gbOG6p24gY2hvIDIgY2h14buXaSBk4buvIGxp4buHdSBDb250cm9sIHbDoCBUcmVhdG1lbnQsIGvhur90IHF14bqjIHh14bqldCByYSBuaMawIHNhdToNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRmICU+JSBmaWx0ZXIoQ29uZCA9PSAiQ29udHJvbCIpICU+JSANCiAgYm9vdF9jb3VudChvcmcgPSAuLA0KICAgICAgICAgICAgIHJlcGxhY2UgPSBGLA0KICAgICAgICAgICAgIHNpemUgPSAxMDAwMCwNCiAgICAgICAgICAgICBzYW1wX3dpbmRvdyA9IDM2MDApJT4lDQogIG11dGF0ZShDb25kID0gIkJhc2VsaW5lIikgLT4gb3V0X2Jhc2UNCg0KaGVhZChvdXRfYmFzZSkNCmBgYA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCg0KZGYgJT4lIGZpbHRlcihDb25kICE9ICJDb250cm9sIikgJT4lIA0KICBib290X2NvdW50KG9yZyA9IC4sDQogICAgICAgICAgICAgcmVwbGFjZSA9IEYsDQogICAgICAgICAgICAgc2l6ZSA9IDEwMDAwLA0KICAgICAgICAgICAgIHNhbXBfd2luZG93ID0gMzYwMCklPiUNCiAgbXV0YXRlKENvbmQgPSAiVHJlYXRtZW50IikgLT4gb3V0X3RyZWF0DQoNCmhlYWQob3V0X3RyZWF0KQ0KYGBgDQoNCkzDumMgbsOgeSwgdGEgxJHDoyBjw7MgdHJvbmcgdGF5IDIgYuG7mSBk4buvIGxp4buHdSB0w6FpIGNo4buNbiBt4bqrdSBjaG8gMiB0aMO0bmcgc+G7kSA6IHThuqduIHN14bqldCBwaMOhdCBzaW5oIERlc2F0dXJhdGlvbiB0cm9uZyAxaCwgdsOgIMSR4buZIGTDoGkgY+G7p2EgZGVzYXR1cmF0aW9uIHRyb25nIDFoLiBW4bubaSB24buLIHRyw60gY+G7p2EgY+G7rWEgc+G7lSBjaOG7jW4gbeG6q3UgdHLDqm4gdHLhu6VjIHRo4budaSBnaWFuLCB0YSBjw7MgdGjhu4MgbcO0IHThuqMgZGnhu4VuIHRp4bq/biAyIHRow7RuZyBz4buRIG7DoHk6DQoNClRoZW8gxJHDsywgYmFuIMSR4bqndSBi4buHbmggbmjDom4gZMaw4budbmcgbmjGsCBjw7Mgc+G7kSBjxqFuIERlc2F0dXJhdGlvbiB0xrDGoW5nIMSRxrDGoW5nIGhv4bq3YyB0aOG6rW0gY2jDrSBjYW8gaMahbiwgbmjGsG5nIHNhdSBraGkgbmfhu6cgMmggdGjDrCDEkWnhu4F1IHRy4buLIGLhuq90IMSR4bqndSBjaG8gdGjhuqV5IGhp4buHdSBxdeG6ozogc+G7kSBjxqFuIGRlc2F0dXJhdGlvbiBnaeG6o20gduG7gSAwIHRyb25nIHN14buRdCAyaCwgY8OzIHTEg25nIG5o4bq5IGzDumMgNC01aCBuaMawbmcgc2F1IMSRw7MgPTAgY2hvIMSR4bq/biBzw6FuZy4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpjb21iX2RmID0gYmluZF9yb3dzKG91dF9iYXNlLCBvdXRfdHJlYXQpDQoNCmNvbWJfZGYgJT4lIGZpbHRlcihFdmVudCA9PSAnWScpJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBMb2MvMzYwMCwgeSA9IEZyZXEsIGNvbCA9IENvbmQpKSArDQogIGdlb21fc3RlcCgpKw0KICB4bGFiKCJUaW1lbGluZSAoaCkiKSsNCiAgeWxhYignSG91cmx5IG9jY3VyaW5nIHJhdGUgKGV2ZW50L2gpJykrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpUcm9uZyBraGkgxJHDsywgdOG7iSBs4buHIHRo4budaSBnaWFuIERlc2F0dXJhdGlvbiAoxJDhu48pIHNvIHbhu5tpIELDrG5oIHRoxrDhu51uZyAoWGFuaCkgY8WpbmcgZ2nhuqNtIMSRw6FuZyBr4buDIGtoaSDEkWnhu4F1IHRy4buLDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kb3V0X2Jhc2UgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBMb2MvMzYwMCwgDQogICAgICAgICAgICAgeSA9IE5vcm1fZHQsIA0KICAgICAgICAgICAgIGZpbGwgPSBFdmVudCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIA0KICAgICAgICAgICBwb3NpdGlvbiA9ICJmaWxsIikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZWVuIiwicmVkIikpKw0KICB4bGFiKCJEdXJhdGlvbiAoaCkiKSsNCiAgdGhlbWVfYncoKQ0KDQpgYGANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpvdXRfdHJlYXQgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBMb2MvMzYwMCwgDQogICAgICAgICAgICAgeSA9IE5vcm1fZHQsIA0KICAgICAgICAgICAgIGZpbGwgPSBFdmVudCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIA0KICAgICAgICAgICBwb3NpdGlvbiA9ICJmaWxsIikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZWVuIiwicmVkIikpKw0KICB4bGFiKCJEdXJhdGlvbiAoaCkiKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCkLhuqNuIHRow6JuIHRo4budaSBnaWFuIGNodeG6qW4gaMOzYSBj4bunYSBEZXNhdHVyYXRpb24gY8WpbmcgdGhlbyBkaeG7hW4gdGnhur9uIHTGsMahbmcgdOG7sSBuaMawIGNo4buJIHPhu5EgdOG6p24gc3XhuqV0IHBow6F0IHNpbmggYmnhur9uIGPhu5EgduG6rXk6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KY29tYl9kZiAlPiUgZmlsdGVyKEV2ZW50ID09ICdZJyklPiUgDQogIGdncGxvdChhZXMoeCA9IExvYy8zNjAwLCB5ID0gTm9ybV9kdCwgY29sID0gQ29uZCkpICsNCiAgZ2VvbV9zdGVwKCkrIA0KICB4bGFiKCJUaW1lbGluZSAoaCkiKSsNCiAgeWxhYignTm9ybWFsaXplZCBldmVudCBkdXJhdGlvbicpKw0KICB0aGVtZV9idygpDQpgYGANCg0Kxq91IMSRaeG7g20gY+G7p2EgcXV5IHRyw6xuaCB0w6FpIGNo4buNbiBt4bqrdSBuZ+G6q3Ugbmhpw6puLCDEkcOzIGzDoCBuw7MgY2hvIHBow6lwIG3DtCB04bqjIMSR4bq3YyB0w61uaCBwaMOibiBi4buRIGPhu6dhIDIgdGjDtG5nIHPhu5EgbMOibSBzw6BuZyBuw6B5LCB2w6AgcXVhIHNvIHPDoW5oIGjDrG5oIOG6o25oIDIgcGjDom4gYuG7kSBuw6B5LCB0YSB0aOG6pXkgxJHGsOG7o2Mgc+G7sSB0xrDGoW5nIHBo4bqjbiBnaeG7r2EgVHLGsOG7m2MgdsOgIFRyb25nIGtoaSDEkWnhu4F1IHRy4buLOg0KDQpUaGVvIMSRw7MsIMSRaeG7gXUgdHLhu4sgYuG6sW5nIG3DoXkgQ1BBUCBsw6BtIGdp4bqjbSDEkcOhbmcga+G7gyB04bqnbiBzdeG6pXQgcGjDoXQgc2luaCBiaeG6v24gY+G7kSBEZXNhdHVyYXRpb24gLyAxaDoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpjb21iX2RmICU+JSBmaWx0ZXIoRXZlbnQgPT0gJ1knKSU+JSANCiAgZ2dwbG90KGFlcyh4ID0gRnJlcSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWxwaGEgPSAwLjUsIGFlcyhmaWxsID0gQ29uZCksIGNvbCA9ICJibGFjayIsIGJpbndpZHRoID0gNSkrDQogIGZhY2V0X3dyYXAofkNvbmQsIG5jb2wgPSAxKSsNCiAgeGxhYignSG91cmx5IG9jY3VyaW5nIHJhdGUgKGV2ZW50L2gpJykrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsImJsdWUiKSkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpOw7MgY8WpbmcgbMOgbSBnaeG6o20gdOG7lW5nIHRo4budaSBnaWFuIERlc2F0dXJhdGlvbiAvIG3hu5dpIGdp4budDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KY29tYl9kZiAlPiUgZmlsdGVyKEV2ZW50ID09ICdZJyklPiUgDQogIGdncGxvdChhZXMoeCA9IE5vcm1fZHQpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSwgYWVzKGZpbGwgPSBDb25kKSwgY29sID0gImJsYWNrIiwgYmlud2lkdGggPSA1KSsNCiAgeGxhYigiTm9ybWFsaXplZCBkdXJhdGlvbiIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIikpKw0KICB0aGVtZV9idygpDQpgYGANCg0KVGEgY8OzIHRo4buDIHRo4buxYyBoaeG7h24gc3V5IGRp4buFbiB0aOG7kW5nIGvDqiBjaG8gZOG7ryBsaeG7h3UgdMOhaSBjaOG7jW4gbeG6q3UgYuG6sW5nIG3DtCBow6xuaCwgdGjDrSBk4bulIG3DtCBow6xuaCBzYXUgxJHDonkgxJHDoW5oIGdpw6EgaGnhu4d1IOG7qW5nIMSRaeG7gXUgdHLhu4sgbMOqbiB04bqnbiBzdeG6pXQgcGjDoXQgc2luaCBkZXNhdHVyYXRpb24gbeG7l2kgZ2nhu50gLSDEkcaw4bujYyBraOG6o28gc8OhdCBuaMawIGJp4bq/biBz4buRIMSR4bq/bTsgbcO0IGjDrG5oIGThu7FhIHbDoG8gcGjDom4gYuG7kSBOZWdhdGl2ZSBCaW5vbWlhbCAoTmjhu4sgdGjhu6ljIMOibSkgY8OzIHjDqXQgdGjDqm0gU3BsaW5lIGZ1bmN0aW9uIGNobyB0aOG7nWkgZ2lhbi4NCg0KROG7sWEgdsOgbyBzdXkgZGnhu4VuIHF1YSBJbmNpZGVuY2UgcmF0ZSByYXRpbywgY8OzIHRo4buDIG7Ds2kgxJFp4buBdSB0cuG7iyBsw6BtIGdp4bqjbSBraG/huqNuZyA1MCUgbmd1eSBjxqEgcGjDoXQgc2luaCBEZXNhdHVyYXRpb24gbeG7l2kgZ2nhu506DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShnYW1sc3MpDQoNCm1vZCA9IGdhbWxzcyhkYXRhID0gY29tYl9kZltjb21iX2RmJEV2ZW50ID09ICJZIixdLCANCiAgICAgICAgICAgICBmb3JtdWxhID0gRnJlcSB+IENvbmQgKyBicyhMb2MpLA0KICAgICAgICAgICAgIGZhbWlseSA9ICJOQklJIikNCg0Kc3VtbWFyeShtb2QpDQpgYGANCg0KVMawxqFuZyB04buxLCB0YSBjw7MgdGjhu4MgZMO5bmcgbcO0IGjDrG5oIHbhu5tpIHBow6JuIGLhu5EgQmV0YSBjaG8gdGjhu51pIGdpYW4gZGVzYXR1cmF0aW9uIC9oIGNodeG6qW4gaMOzYSAoZGFvIMSR4buZbmcgdOG7qyAwIMSR4bq/biAxKS4gTcO0IGjDrG5oIGNobyB0aOG6pXkgxJFp4buBdSB0cuG7iyBsw6BtIGdp4bqjbSAwLjQ2IGzhuqduIHRo4budaSBnaWFuIERlc2F0dXJhdGlvbiAvIGgNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCm1vZCA9IGdhbWxzcyhkYXRhID0gY29tYl9kZltjb21iX2RmJEV2ZW50ID09ICJZIixdLCANCiAgICAgICAgICAgICBmb3JtdWxhID0gTm9ybV9kdCB+IENvbmQgKyBicyhMb2MpLA0KICAgICAgICAgICAgIGZhbWlseSA9ICJCRSIpDQoNCnN1bW1hcnkobW9kKQ0KYGBgDQoNCiMgVHJ1bmcgYsOsbmggdsOgIHTDoWkgY2jhu41uIG3huqt1DQoNCuG7niB0csOqbiwgY2jDum5nIHRhIHThuqFvIHJhIHRow7RuZyBz4buRIGzDom0gc8OgbmcgZOG7sWEgdsOgbyBjaHXhu5dpIGdpw6EgdHLhu4sgxJHhu4tuaCB0w61uaCwgYsOieSBnaeG7nSB0YSBz4bq9IHjDqXQgxJHhur9uIGNodeG7l2kgxJHhu4tuaCBsxrDhu6NuZywgbMOgIGNow61uaCBi4bqjbiB0aMOibiBTYU8yLg0KDQrDgXAgZOG7pW5nIGPGoSBjaOG6vyB0xrDGoW5nIHThu7EsIE5oaSBz4bq9IHThuqFvIHJhIG3hu5l0IGNo4buJIHPhu5EgbMOibSBzw6BuZyBjw7MgdMOqbiBsw6A6IFNhTzIgdHJ1bmcgYsOsbmggLyAxMCBwaMO6dDsgY8OhYyBi4bqhbiBjw7JuIG5o4bubIG3DrG5oIMSRw6MgZ2hpIG5o4bqtbiBzaW5oIGhp4buHdSBjaG8gYuG7h25oIG5ow6JuIGLhurFuZyBjw6FjaCDEkW8gxJHhur9tIHRo4bunIGPDtG5nIHRyb25nIDEgcGjDunQsIOG7nyDEkcOieSB0YSBjxaluZyBsw6BtIHTGsMahbmcgdOG7sSwga2hpIMSR4bq3dCBt4buZdCBj4butYSBz4buVIGNo4buNbiBt4bqrdSBuZ+G6q3Ugbmhpw6puIGTDoGkgMTAgcGjDunQgKDYwMCBxdWFuIHPDoXQpIHThuqFpIG5o4buvbmcgduG7iyB0csOtIGLhuqV0IGvDrCB0csOqbiBjaHXhu5dpIHRo4budaSBnaWFuLiDEkOG7gyBsw6BtIHZp4buHYyBuw6B5LCBOaGkgdmnhur90IHRow6ptIDEgaMOgbSBu4buvYSBuaMawIHNhdToNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpib290X21lYW4gPSBmdW5jdGlvbihvcmcgPSBOVUxMLHJlcGxhY2UgPSBULHNpemUgPSAxMCwgc2FtcF93aW5kb3cgPSA2MDApew0KICANCiAgc2FtcGxlX2lkeCA9IHNhbXBsZShjKDE6KG5yb3cob3JnKS0gc2FtcF93aW5kb3cgLSAxKSksIA0KICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSByZXBsYWNlLCANCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gc2l6ZSkNCiAgDQogIG91dF9kZiA9IGRhdGEuZnJhbWUoTWVhbl9TYU8yID0gcmVwKE5BLHNpemUpLExvYyA9IHJlcChOQSxzaXplKSkNCiAgDQogIGkgPSAxDQogIA0KICB3aGlsZSAoaSA8PSBsZW5ndGgoc2FtcGxlX2lkeCkpew0KICAgIHNhbXBfZGYgPSBvcmdbYyhzYW1wbGVfaWR4W2ldIDogKHNhbXBsZV9pZHhbaV0rc2FtcF93aW5kb3cgLSAxKSksXQ0KICAgIA0KICAgIG91dF9kZiRNZWFuX1NhTzJbaV0gPSBtZWFuKHNhbXBfZGYkU2FPMikNCiAgICBvdXRfZGYkTG9jW2ldID0gbWVhbihzYW1wX2RmJFRpbWUpDQogICAgDQogICAgaSA9IGkgKyAxDQogIH0NCiAgDQogIHJldHVybihuYS5vbWl0KG91dF9kZikpDQp9DQpgYGANCg0KTmhpIGzhuqduIGzGsOG7o3Qgw6FwIGThu6VuZyBow6BtIGJvb3RfbWVhbiBuw6B5IGNobyAyIGNodeG7l2kgQ29udHJvbCB2w6AgVHJlYXRtZW50LCBsxrB1IMO9IHTDuXkgY2jhu4luaCByZXBsYWNlID0gVCBjaG8gdGjhuqV5IMSRw6J5IGzDoCAxIHF1eSB0csOsbmggQm9vdHN0cmFwIDEwIG5nw6BuIGzGsOG7o3QNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpkZiAlPiUgZmlsdGVyKENvbmQgIT0gIkNvbnRyb2wiKSAlPiUgDQogIGJvb3RfbWVhbihvcmcgPSAuLA0KICAgICAgICAgICAgIHJlcGxhY2UgPSBULA0KICAgICAgICAgICAgIHNpemUgPSAxMDAwMCwNCiAgICAgICAgICAgICBzYW1wX3dpbmRvdyA9IDYwMCklPiUNCiAgbXV0YXRlKENvbmQgPSAiVHJlYXRtZW50IikgLT4gc2FfdG0NCg0KaGVhZChzYV90bSkNCg0KZGYgJT4lIGZpbHRlcihDb25kID09ICJDb250cm9sIikgJT4lIA0KICBib290X21lYW4ob3JnID0gLiwNCiAgICAgICAgICAgICByZXBsYWNlID0gVCwNCiAgICAgICAgICAgICBzaXplID0gMTAwMDAsDQogICAgICAgICAgICAgc2FtcF93aW5kb3cgPSA2MDApJT4lDQogIG11dGF0ZShDb25kID0gIkNvbnRyb2wiKSAtPiBzYV9iYXNlDQoNCmhlYWQoc2FfYmFzZSkNCmBgYA0KDQpUxrDGoW5nIHThu7EsIHRhIGPDsyB0aOG7gyBraOG6o28gc8OhdCBow6xuaCDhuqNuaCBwaMOibiBi4buRIGPhu6dhIGNo4buJIHPhu5EgbMOibSBzw6BuZyB24burYSB04bqhbyByYSBsw6AgU2FPMiB0cnVuZyBiw6xuaCB0cm9uZyAxMCBwaMO6dCAobMawdSDDvSBsw6AgYuG6oW4gY8OzIHRo4buDIGNo4buNbiBrw61jaCB0aMaw4bubYyBj4butYSBz4buVIG7DoHkgdMO5eSDDvSwgMSBwaMO6dCwgNSBwaMO6dCwgMTAgcGjDunQsIHRo4bqtbSBjaMOtIDMwIHBow7p0KQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmNvbWJfc2EgPSBiaW5kX3Jvd3Moc2FfYmFzZSwgc2FfdG0pDQoNCmNvbWJfc2EgJT4lIGdncGxvdChhZXMoeCA9IE1lYW5fU2FPMiwgZmlsbCA9IENvbmQpKSsNCiAgZ2VvbV9oaXN0b2dyYW0oYWxwaGEgPSAwLjUsIGNvbCA9ICJibGFjayIpKw0KICB4bGFiKCdTYU8yIC8gMTAgbWluJykrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpgYGB7cn0NCmNvbWJfc2EgJT4lDQogIGdncGxvdChhZXMoeCA9IENvbmQsIHkgPSBNZWFuX1NhTzIpKSArDQogIGdlb21fbHYoYWxwaGEgPSAwLjUsIGFlcyhmaWxsID0gQ29uZCksIGNvbCA9ICJibGFjayIpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCg0KVGEgY8Wpbmcga2jDtG5nIG3huqV0IHRow7RuZyB0aW4gduG7gSB54bq/dSB04buRIHRo4budaSBnaWFuLCB2w6wgduG7iyB0csOtIGNo4buNbiBt4bqrdSDEkcaw4bujYyBsxrB1IGzhuqFpIHRyb25nIGvhur90IHF14bqjLCB0YSBjw7MgdGjhu4Mga2jhuqNvIHPDoXQgZGnhu4VuIHRp4bq/biBj4bunYSBTYU8yLzEwcGggDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KY29tYl9zYSAlPiUgDQogIGdncGxvdChhZXMoeCA9IExvYy8zNjAwLCB5ID0gTWVhbl9TYU8yLCBjb2wgPSBDb25kKSkgKw0KICBnZW9tX3N0ZXAoKSsgDQogIHhsYWIoIlRpbWVsaW5lIChoKSIpKw0KICB5bGFiKCdTYU8yIC8gMTAgbWluJykrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIELDoG4gbHXhuq1uDQoNClRow7RuZyDEkWnhu4dwIHF1YW4gdHLhu41uZyBuaOG6pXQgbcOgIE5oaSBtdeG7kW4gY2hpYSBz4bq7IHbhu5tpIGPDoWMgYuG6oW4gcXVhIGLDoGkgbsOgeSwgxJHDsyBsw6Aga+G7uSB0aHXhuq10IGzhuq1wIHRyw6xuaCB0aOG7kW5nIGvDqiBjw7MgdGnhu4FtIG7Eg25nIHbDtCBjw7luZyBs4bubbi4gTsOzIGtow7RuZyBjaOG7iSBjaG8gcGjDqXAgbOG6rXAga+G6vyBob+G6oWNoIHBow6JuIHTDrWNoIHRyw6puIG5o4buvbmcgYuG6o25nIGThu68gbGnhu4d1IHRow7RuZyBz4buRIGzDom0gc8OgbmcgY8OzIHPhurVuLCBtw6AgY8OybiBjaG8gcGjDqXAgdOG6oW8gcmEgY+G6oyBuaOG7r25nIMSR4bqhaSBsxrDhu6NuZyBob8OgbiB0b8OgbiBt4bubaSB04burIGThu68gbGnhu4d1IHRow7QuIEPGoSBjaOG6vyDEkcaw4bujYyBz4butIGThu6VuZyB0cm9uZyBiw6BpIHLhuqV0IMSRxqFuIGdp4bqjbiwgxJHDsyBsw6AgdMOhaSBjaOG7jW4gbeG6q3Ugbmfhuqt1IG5oacOqbiBob+G6t2MgYm9vdHN0cmFwLCB24bubaSBt4buZdCDDrXQga+G7uSB0aHXhuq10IHbhu4EgdsOybmcgbOG6t3AgdHJvbmcgUiBuaMawbmcga+G6v3QgcXXhuqMgdOG6oW8gcmEgbMOgIGtow6Eg4bqlbiB0xrDhu6NuZy4gR2hpIGNow7ogdGjDqm0gbMOgIHRow60gZOG7pSBtaW5oIGjhu41hIG7DoHkgY2jhu4kgbeG7m2kgbMOgIG3hu5l0IGNodeG7l2kgbmjhu4sgcGjDom4sIHR1eSBuaGnDqm4ga+G7uSB0aHXhuq10IHTDoWkgY2jhu41uIG3huqt1IGPDsyB0aOG7gyBt4bufIHLhu5luZyBjaG8gY2h14buXaSDEkWEgc+G7sSBraeG7h24gKG5oaeG7gXUgbG/huqFpIGJp4bq/biBj4buRKSwgDQoNCkNow7pjIGPDoWMgYuG6oW4gduG7r25nIGLGsOG7m2MgdHLDqm4gaMOgbmggdHLDrG5oIGtob2EgaOG7jWMgduG7m2kgUg==