1 Giới thiệu

Thân chào các bạn, đây là bài thực hành thứ hai trong dự án Bayes for Vietnam, được reboot lại từ project cùng tên năm 2016. Trong bài trước, Nhi đã giới thiệu một số thông tin cơ bản về trường phái Bayes, một cách tiếp cận khác cho những phân tích và suy diễn thống kê. Nguyên lý của phương pháp này dựa vào định lý Bayes, được diễn đạt bằng ngôn ngữ mô hình, xác suất điều kiện. Suy diễn thống kê theo Bayes liên kết giữa 4 phần: Dữ liệu quan sát thực tế, giả thuyết (phân phối) tiền định Prior, hàm likelihood và kết quả phân phối hậu định.

Bản thân trường phái frequentist và null hypothesis testing (NHST) không có gì sai cả, nhưng nó không đủ khả năng để giải quyết những khủng hoảng và bất cập trong suy diễn thống kê ứng dụng vào nghiên cứu. Khái niệm ý nghĩa thống kê và ngưỡng p_value < 0.05 đã sinh ra nhiều hệ quả tai hại, gồm những cách gian lận rất đáng trách, thí dụ :

  • Bịa đặt giả thuyết H0 và H1 SAU KHI nhìn thấy p_value

  • Thực hiện hàng loạt kiểm định theo kiểu ăn may (tra tấn dữ liệu), dấu kết quả p>0.05 và khoe kết quả p<0.05

  • Hack trị số p (và cả CI) để có ý nghĩa thống kê bằng mọi giá

Như vậy NHST không giải quyết mọi vấn đề :thí dụ ý nghĩa thống kê hoàn toàn khác với ý nghĩa lâm sàng, mà nó còn gây ra vấn đế mới. Nó cũng gây ngộ nhận vì không có nhiều người hiểu bản chất p-value là gì.

Bayes có 2 ưu điểm mà không thể có trong trường phái cũ (frequentist), đó là:

  1. Hòa hợp giữa giả định/kết quả có trước và dữ liệu thực tế,

  2. Rất linh hoạt: Bayes không còn bị ràng buộc bởi các giả định điều kiện như phân phối chuẩn, outliers, cỡ mẫu, phương sai đồng nhất…; và khi có phân phối hậu định trong tay, có nhiều cách để diễn giải, kiểm định giả thuyết nghiên cứu và mỗi cách đều khắc phục các bất cập của trị số p.

2 Ngôn ngữ STAN

Có nhiều cách để thực hành Bayes. Đơn giản nhất là dùng những chương trình (package) viết sẵn, thí dụ BayesFactor, MCMC hoặc các package chuyên biệt cho từng loại model. Các công cụ này đưa bạn thẳng tới kết quả cuối cùng là Bayes factor của tham số, nhưng không cho biết cơ chế hoạt độngbên trong và bạn cũng không tùy chỉnh được cơ chế này.

Phức tạp hơn, chúng ta có những ngôn ngữ lập trình cho Gibs sampler như BUGs và JAGs, chúng đều có giao thức trong R và các package ứng dụng. Cuối cùng, chúng ta có ngôn ngữ STAN với cơ chế hoàn toàn khác, và STAN cũng có giao thức và package ứng dụng trong R như rstan, rstanarm, brms…

Năm 2013, Nhi bắt đầu học Bayes, vừa đọc lý thuyết, vừa viết code bằng WinBUGs, sau đó là JAGs. Cuốn sách của J . Kruschke và Richard McElreath đã cải đạo cho Nhi từ tín đồ của phái Frequentist thành tín đồ của phái Bayes. Sau đó STAN ra đời, cùng với 2nd edition của cuốn sách của Kruschke và hàng loạt các giao thức STAN trong R đã cho phép Nhi thực hành Bayes một cách thoải mái hơn. Do STAN có nhiều ưu điểm và được hỗ trợ đầy đủ nên dự án Bayes for Vietnam chọn STAN làm ngôn ngữ cho tutorial.

STAN là một ngôn ngữ lập trình thống kê xác suất, được phát triển cho R, Python và Matlab.Cơ chế tạo ra chuỗi Markov Monte Carlo của STAN hoàn toàn khác biệt với JAGs và BUGs là 2 sampler có trước. STAN sử dụng phương pháp Hamiltonian Monte Carlo (HMC) sampling dựa vào lý thuyết của Metropolis-Hastings (1953, 1970). Bạn có thể install STAN từ đây:

https://github.com/stan-dev

3 T test

Kể từ lúc William Sealy Gosset (1908) phát minh ra kiểm định t và phân phối t dưới bút danh là Student, t test đã trở thành công cụ được sử dụng phổ biến nhất trong hầu hết các nghiên cứu y học, bao gồm luận văn của các sinh viên. Mục đích giản dị của t test là để so sánh giá trị trung bình giữa 2 phân nhóm dựa vào null hypothesis testing trên trị số t. Thực ra test t chính là một mô hình tuyến tính mô tả biến kết quả Y theo 2 biến số giả chỉ phân nhóm. Trong R, ta có thể thực hiện test t dựa vào công cụ trực tiếp hoặc qua mô hình glm.

Mục tiêu của bài thực hành này là giới thiệu một cách tiếp cận hoàn toàn mới theo trường phái Bayes để so sánh 2 phân nhóm, thay thế cho test t.

Do bài toán này rất phổ biến, đã có nhiều công cụ đã được tạo ra trong R cho phép làm trực tiếp t-test Bayes, thí dụ package BayesFactor, BEST… Tuy nhiên, Nhi sẽ dẫn các bạn đi theo con đường dài hơn, làm thủ công mọi thứ từ code mô hình cho đến diễn giải kết quả, nhưng đổi lại, các bạn sẽ hiểu rõ hơn về cơ chế của mô hình, cấu trúc STAN code, về prior, và kiểm soát mọi thứ, kể cả vẽ biểu đồ theo ý thích.

Nhi cũng sẽ giới thiệu với các bạn cả 3 cách suy diễn Bayes, dùng Bayes Factor, ROPE và ngưỡng so sánh. Khi kết thúc bài thực hành, các bạn sẽ có trong tay một quy trình hoàn chỉnh thay thế hoàn toàn t test cổ điển bằng t test Bayes.

4 Từ t test đến t test Bayes

Một cách tổng quát, suy diễn Bayes dựa trên định lý Bayes như sau :

\[ p(\theta |y,x)\propto p(y|\theta ,x) p(\theta ,x) \] theo đó, phân phối hậu định của một tham số Theta (khi ta có trong tay dữ liệu biến kết quả Y và hằng số X là một matrix các predictors trong mô hình) tỉ lệ với tích của hàm likelihood (xác suất có điều kiện của y khi có theta và x) và phân phối tiền định (prior= giả thuyết về phân phối của theta).

Nếu các bạn tiếp cận được với những tài liệu Thống kê độc đáo, như giáo trình SPSS và R của Andy Field hay quyển Statistical Rethinking của Richard McElreath, bạn sẽ nhận ra rằng quy trình so sánh một biến định lượng giữa các phân nhóm thực chất là một mô hình hồi quy tuyến tính. Suy rộng ra, gần như tất cả những câu hỏi nghiên cứu đều có thể diễn đạt bằng ngôn ngữ mô hình. Do đó, học về mô hình là một cách đầu tư khôn ngoan vì nó trang bị cho các bạn một công cụ phổ quát để tiếp cận vấn đề.

Trong bài toán so sánh đại lượng Y giữa các phân nhóm, mô hình của chúng ta sẽ gồm những yếu tố sau đây :

Một đại lượng Y là biến liên tục mà ta muốn khảo sát, ta gọi nó là biến kết quả (outcome). Giá trị outcome Y này được mô tả bằng một kiểu phân phối xác định.

Theo tinh thần của t test của Gosset, outcome là giá trị trung bình của biến Y, và khác biệt trung bình giữa 2 phân nhóm G1 và G2. Khác biệt trung bình này được mô tả bằng 1 phân phối t. Như ta biết, phân phối t có 3 tham số là mu (location), sigma (scale) và kiểu hình (hay độ tự do) Nu.

Mục tiêu của t test Bayes là tìm phân phối hậu định của 3 tham số này.Để làm việc đó, ta phải dựng mô hình trong STAN

5 Dữ liệu minh họa

Bài toán minh họa của chúng ta là dataset ICU, đây là một nghiên cứu mô tả cắt ngang, khảo sát huyết áp và mạch của 200 bệnh nhân shock nhiễm khuẩn trong khoa ICU. Câu hỏi nghiên cứu giả định của chúng ta là : So sánh huyết áp tâm thu (SysBP) giữa 2 phân nhóm bệnh nhân Tử vong và Sống sót.

# The Data and Story Library (DASL), http://lib.stat.cmu.edu/DASL/Datafiles/ICU.html

df=read.csv("https://raw.github.com/vincentarelbundock/Rdatasets/master/csv/Stat2Data/ICU.csv")%>%as_tibble()

df$Outcome<-recode_factor(df$Survive,
                         `0`="Dead",
                         `1`="Survived")

df$Survive<-recode_factor(df$Survive,
                          `0`="1",
                          `1`="2")

psych::describeBy(df$SysBP,df$Survive)
## 
##  Descriptive statistics by group 
## group: 1
##    vars  n   mean    sd median trimmed   mad min max range skew kurtosis
## X1    1 40 118.83 41.08    126  117.22 32.62  36 256   220  0.6      1.4
##     se
## X1 6.5
## -------------------------------------------------------- 
## group: 2
##    vars   n   mean   sd median trimmed   mad min max range skew kurtosis
## X1    1 160 135.64 29.8    132  133.97 29.65  48 224   176 0.42     0.34
##      se
## X1 2.36
df%>%ggplot(aes(x=SysBP,fill=Outcome))+
  geom_density(alpha=.6,col="black")+
  geom_histogram(aes(y=..density..,col=Outcome),alpha=0.5)+
  facet_wrap(~Outcome,ncol=1,scales="free_y")+
  theme_bw()

6 t-test theo trường phái frequentist

Đầu tiên, Nhi sẽ làm t-test theo trường phái cổ điển

t.test(df$SysBP~df$Outcome,alternative="less")
## 
##  Welch Two Sample t-test
## 
## data:  df$SysBP by df$Outcome
## t = -2.4341, df = 49.726, p-value = 0.009281
## alternative hypothesis: true difference in means is less than 0
## 95 percent confidence interval:
##       -Inf -5.237818
## sample estimates:
##     mean in group Dead mean in group Survived 
##               118.8250               135.6438
effectsize=function(outcome,factor,data){
  d=data
  n2=Hmisc::describe(d[factor])%>%.[1]%>%.[[factor]]%>%.$values%>%.$frequency%>%.[1]
  n1=Hmisc::describe(d[factor])%>%.[1]%>%.[[factor]]%>%.$values%>%.$frequency%>%.[2]
  var=d[outcome]%>%split(d[factor])%>%map(~var(.))
  v2=var[[1]]%>%.[1]
  v1=var[[2]]%>%.[1]
  m2=d[outcome]%>%split(d[factor])%>%map(~psych::describe(.))%>%.[1]%>%.[[1]]%>%.$mean
  m1=d[outcome]%>%split(d[factor])%>%map(~psych::describe(.))%>%.[2]%>%.[[1]]%>%.$mean
  s=sqrt(((n1-1)*v1+(n2-1)*v2)/(n1+n2-2))
  cohend=((m1-m2)/s)
  hedgeG=((m1-m2)/s)*(1- (3/(4*(n1+n2)-9)))
  cat("Effect size for Independent t test","\n")
  cat("Cohen's d value=",cohend,"\n")
  cat("Hedge's G=",hedgeG,"\n")
}

effectsize(outcome="SysBP",factor="Outcome",data=df)
## Effect size for Independent t test 
## Cohen's d value= 0.5201267 
## Hedge's G= 0.5181541

Kết quả cho thấy huyết áp giảm một cách có ý nghĩa thống kê ở nhóm bệnh nhân tử vong so với nhóm sống sót (t=-2.43, p=0.0093, Cohen’s d=0.52).

7 Viết code STAN cho t-test BAYES

Trước khi tiếp cận dữ liệu (data), ta đã có thể đặt giả thuyết về mô hình. Trạng thái sơ khai này của mô hình được xác định bằng các priors (phân phối tiền nghiệm, hay tiền định) của Outcome và tất cả các Tham số trong mô hình

library(rstan)

stanmodelcode ="

data {
int<lower=1> N;                               // Co mau (lower=1: de kiem tra null data ?)
int<lower=2> Ng;                              // So phan nhom (lower =2: it nhat phai co 2 phan nhom)
real<lower=5> n1;                              // Co mau phan nhom 1: it nhat phai co 5 cases
real<lower=5> n2;                              // Co mau phan nhom 2: it nhat phai co 5 cases
vector[N] y;                                  // Bien ket qua Y la 1 vector co do dai=N
int<lower=1, upper=2> groupID[N];            //  group ID la 1 so nguyen nhan 2 gia tri =1 hoac =2
}

transformed data{
real meany;                                   // Bien meany la trung binh cua Y la 1 so thuc; prior cua no la mu
meany = mean(y);                              // Xac dinh gia tri meany= ham mean
}

parameters {
vector[2] mu;                                 // Khai bao tham so cua phan phoi Student-t: mu, sigma va nu; mu la 1 vector 2 gia tri
vector<lower=0>[2] sigma;                     // sigma la 1 vector, gioi han duoi =0, nhan 2 gia tri
real<lower=0, upper=100> nu;                  // nu la do tu do hay kieu hinh cua phan phoi t, gioi han 0:100
}

transformed parameters {                        // Phan hoan chuyen tham so cua mo hinh stan: khong can thiet
}

model {                                         // Khai bao noi dung mo hinh 
// khai bao tien dinh (prior)
mu ~ normal(meany, 10);                       // Tien dinh (prior) cua tham so mu la phan phoi Gaussian, tb=meany, sd=10
sigma ~ cauchy(0, 5);                         // Tien dinh cua tham so Sigma la pp Cauchy(0,5)
nu ~ exponential(1.0/29);                     // Tien dinh cua tham so Nu la pp Exponential voi rate=29


// khai bao likelihood = 1 vong lap

for (n in 1:N){
y[n] ~ student_t(nu, mu[groupID[n]], sigma[groupID[n]]);
}

}

generated quantities {                        // Tinh toan cac tham so trong t_test 
vector[N] Res;                               // Phan phoi hau dinh cua phan vi gia tri du bao Y theo mo hinh, yRep la 1 vector do dai N
real muDiff;                                  // Khac biet trung binh : muDiff la 1 so thuc
real s;                                       // pooled sd
real Cohensd;                                 // Cohen's d effect size (Jacob Cohen) la 1 so thuc
real HedgesG;                                 // Hedges's G effect size ( Larry Hedges, 1981), la so thuc

for (n in 1:N){
  Res[n] = student_t_rng(nu, mu[groupID[n]], sigma[groupID[n]]);
}

muDiff = mu[2] - mu[1];
s=sqrt(((n1-1)*sigma[1]^2+(n2-1)*sigma[2]^2)/(n1+n2-2));      //Tinh pooled sd
Cohensd = muDiff / s;                                      // Tinh Cohen's d
HedgesG = (muDiff / s)*(1- (3/(4*(n1+n2)-9)));                                    // Tinh Hedges's G
}
"

Một chương trình (program) Bayes trong ngôn ngữ STAN có cấu trúc nhiều phần, với danh pháp như sau:

  1. Phần data: có nội dung là khai báo các tham số về dữ liệu, mỗi tham số phải được định danh, và định dạng. Trong thí dụ này, có 6 tham số data là: cỡ mẫu N (là 1 số nguyên >1), số phân nhóm Ng (số nguyên dương > 2), cỡ mẫu mỗi phân nhóm (n1,n2) – lẽ ra cũng là số nguyên, nhưng được định dạng là số thực (real), vì Nhi muốn dùng chúng để tính effect size (Cohen’s d), và trong STAN các phép tính số nguyên sẽ cho ra kết quả là số nguyên, làm tròn, là điều ta không mong muốn. Biến kết quả y là 1 vector kích thước N, Tên phân nhóm groupID là 1 số nguyên, nhận 2 giá trị 1, 2.

  2. Phần transformed data để thực hiện hoán chuyển dữ liệu (thường là biến kết quả) trước khi đưa vào hàm likelihood. Việc hoán chuyển này có bản chất là áp dụng 1 link function lên Y. Trong thí dụ này, outcome của mô hình t-test là 1 giá trị trung bình của Y, nên ta khai báo biến outcome sau hoán chuyển là meany (1 số thực), và công thức hoán chuyển : mean y = mean(y).

  3. Phần tham số (parameters): Đây là những tham số mà ta cần tìm phân phối hậu định, kết quả của mô hình Bayes. Trong thí dụ này, ta có 3 tham số, mu, sigma và nu tương ứng với location, scale và shape của một phân phối Student t. mu là 1 vector, sigma là 1 vector, có 2 nhánh cho 2 phân nhóm (như vậy kết quả sẽ có mu1, mu2, sigma1, sigma2); nu là 1 số thực giới hạn từ 0-100.

  4. Phần tham số hoán chuyển (transformed parameters): không dùng đến ở đây nên được để trống

  5. Phần model : nội dung của mô hình Bayes: Đầu tiên, ta khai báo Priors cho mỗi tham số. Nhắc lại : Prior là một hàm mật độ xác suất mà ta giả định (tin) rằng hợp lý nhất cho tham số. Prior là thông tin có trước khi nhìn thấy kết quả, nó có thể là kết quả trong quá khứ (y văn), kinh nghiệm cá nhân, hoặc giả định lý thuyết. Ở đây, ta lần lượt đặt 3 prior cho 3 tham số mu, sigma và nu như sau:

Mu là location của phân phối t, của biến kết quả Mean(Y). Vì mu là 1 số thực, và sự biến thiên ngẫu nhiên của nó có bản chất là kết quả của một quá trình thêm/bớt những cá thể có giá trị y cao/thấp một cách ngẫu nhiên, do đó phân phối hợp lý nhất của mu sẽ là Gaussian (normal). Thật vậy, Gaussian là phân phối hợp lý nhất cho 1 biến liên tục khi mà ta không có khả năng đưa ra bất cứ giả định nào khác, nó là quy luật của mọi hiện tượng sinh lý, tự nhiên. Phân phối Gaussian (normal) này có location= trung bình của y và scale=10.

Prior của sigma và nu khó giải thích hơn (Nhi không có giả định nào nên buộc phải dùng phân phối cauchy cho sigma và exponential với rate =29 cho nu, 2 prior này đã được kiểm chứng trong package BEST và tài liệu của Kruschke). Để dễ hình dung, ta có thể thử vẽ density plot của 2 prior này. Ghi chú là bạn có thể dùng phân phối Gamma thay cho exponential cũng được.

p1=rexp(5000,1/29)%>%
  as.data.frame()%>%
  ggplot(aes(x=.))+
  geom_density(alpha=0.5,col="black",fill="red")+
  theme_bw()+ggtitle("Prior cho nu")

p2=rnorm(5000,mean(df$SysBP),10)%>%
  as.data.frame()%>%
  ggplot(aes(x=.))+
  geom_density(alpha=0.5,col="black",fill="red")+
  theme_bw()+ggtitle("Prior cho Mu")

p3=rcauchy(5000,location = 0,scale =5)%>%
  as.data.frame()%>%
  ggplot(aes(x=.))+xlim(c(0,max(df$SysBP)))+
  geom_density(alpha=0.5,col="black",fill="red")+
  theme_bw()+ggtitle("Prior cho Sigma")

p4=df%>%ggplot(aes(x=SysBP))+
  geom_density(alpha=0.5,col="black",fill="red")+
  theme_bw()+ggtitle("Phân phối thực tế của Y")

gridExtra::grid.arrange(p1,p2,p3,p4,ncol=2)
## Warning: Removed 2486 rows containing non-finite values (stat_density).

Nếu không có prior, STAN sẽ dùng flat prior, nhưng kết quả sẽ không chính xác

Sau khi khai báo prior, ta viết 1 vòng lặp cho hàm likelihood : nội dung của nó là ước lượng giá trị của outcome y với quy luật phân phối student-t được xác định bằng 3 tham số mu, sigma, nu, tùy theo phân nhóm groupID.

Đến đây là hết nội dung model.

  1. Phần cuối cùng của chương trình STAN có tên là generated quantities, đây là một chức năng rất hữu dụng, vì nó cho phép chúng ta tạo ra bất kì trị số thống kê mà ta thích dựa vào mô hình. Đây chính là giao điểm giữa trường phái Bayes và thống kê cổ điển, ta có thể rút ra phân phối hậu định của những trị số thống kê cho t-test mà ta từng biết, như :

Phân phối hậu định của giá trị Y cho mỗi cá thể trong mẫu : Res

Phân phối hậu định cho khác biệt trung bình : muDiff = mu phân nhóm 2 – mu phân nhóm 1

Phân phối hậu định cho Effect -size của t test : Cohen’s d và Hedge’s G

(nếu thích, bạn có thể tính cả p_value cho 1 null hypothesis testing: thí dụ H1=muDiff > 0, chẳng hạn, nhưng Nhi không muốn đi lạc quá xa về phái frequentist ở đây).

8 Thi hành mô hình STAN

Tác giả John Kruschke đã chia sẻ kinh nghiệm của ông khi viết code cho mô hình STAN : đi ngược từ likelihood model lên trên, và di chuyển con trỏ chuột giữa các block, để đảm bảo không sót, không sai (chỉ cần thiếu một dấu ; hay {, chương trình sẽ không thể compile được và báo lỗi, dĩ nhiên đây là điều tốt, vì quy trình sampling MCMC rất tốn kém thời gian, nên STAN kiểm tra lỗi cú pháp trước khi thi hành.

Sau khi đã đóng gói mô hình, ta khai báo data cho mô hình trong 1 list, đây chính là các tham số trong block data. Thí dụ ở đây : N=200, Ng=2, n1=40, n2=160, groupID là một vector số nguyên từ column Survive trong dataframe, y là vector của column SysBP trong data frame.

Bây giờ ta đưa đoạn code và data vào hàm stan để bắt đầu thi hành mô hình. Trước tiên, STAN sẽ kiểm tra cú pháp của đoạn code, tương hợp giữa các tham số trong đoạn code và list data. Nếu tất cả đều hợp chuẩn, đoạn code và data sẽ được compile thành 1 program C++, và thi hành. Program này kích hoạt một sampler để bắt đầu lấy mẫu các chuỗi MCMC theo cơ chế mà ta đã đưa ra, thí dụ : 10.000 lượt iteration, khởi động 1000 lượt, chạy song song trên 4 cores, và rút gọn MCMC (thin =10).

groupID=as.numeric(df$Survive)

data= list(N=200, Ng=2, n1=40, n2=160, groupID=groupID, y=df$SysBP)

set.seed(1234)

fit = stan(model_code=stanmodelcode, data=data, iter=10000, warmup=1000, cores=4, thin=10)
## In file included from C:/Users/Admin/Documents/R/win-library/3.4/BH/include/boost/config.hpp:39:0,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/BH/include/boost/math/tools/config.hpp:13,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/stan/math/rev/core/var.hpp:7,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/stan/math/rev/core/gevv_vvv_vari.hpp:5,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/stan/math/rev/core.hpp:12,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/stan/math/rev/mat.hpp:4,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/stan/math.hpp:4,
##                  from C:/Users/Admin/Documents/R/win-library/3.4/StanHeaders/include/src/stan/model/model_header.hpp:4,
##                  from file1f5c17726c4c.cpp:8:
## C:/Users/Admin/Documents/R/win-library/3.4/BH/include/boost/config/compiler/gcc.hpp:186:0: warning: "BOOST_NO_CXX11_RVALUE_REFERENCES" redefined
##  #  define BOOST_NO_CXX11_RVALUE_REFERENCES
##  ^
## <command-line>:0:0: note: this is the location of the previous definition
## cc1plus.exe: warning: unrecognized command line option "-Wno-ignored-attributes"

Mô hình chỉ mất vài phút để converge, bạn sẽ có 1 object khoảng 6.5 Mb, bên trong nó là các chuỗi MCMC cho tất cả những tham số, trị số mà bạn đã khai báo. Cụ thể, nó chứa 200 chuỗi MCMC cho giá trị dự báo SysBP của từng cá thể (biến Res), 2 chuỗi MCMC cho mu1, mu2, 2 chuỗi cho sigma1, sigma2, 1 chuỗi cho muDiff (kết quả chính của t test), 1 chuỗi cho Cohen’s d và 1 chuỗi cho Hedge’s G

9 Tham dò kết quả phân phối hậu nghiệm

Đầu tiên, chúng ta xem qua phân phối hậu định của mu, sigma, muDiff (kết quả chính), nu và effect size Cohen’s d, Hedges’ G

print(fit, digits=3, pars=c('mu', 'sigma', 'muDiff', 'Cohensd', 'HedgesG','nu'))
## Inference for Stan model: 999c243cf5747c34c22dbcd0b41da6a4.
## 4 chains, each with iter=10000; warmup=1000; thin=10; 
## post-warmup draws per chain=900, total post-warmup draws=3600.
## 
##             mean se_mean     sd    2.5%     25%     50%     75%   97.5%
## mu[1]    122.336   0.091  5.443 111.888 118.691 122.137 125.941 133.372
## mu[2]    134.670   0.039  2.292 130.225 133.129 134.613 136.191 139.233
## sigma[1]  37.172   0.088  5.233  27.840  33.537  36.974  40.365  48.359
## sigma[2]  27.477   0.035  2.123  23.214  26.096  27.556  28.908  31.607
## muDiff    12.333   0.098  5.901   0.391   8.419  12.435  16.325  23.448
## Cohensd    0.418   0.003  0.203   0.013   0.282   0.419   0.555   0.809
## HedgesG    0.417   0.003  0.202   0.013   0.281   0.417   0.553   0.806
## nu        20.898   0.295 17.375   4.576   9.101  14.625  26.419  71.901
##          n_eff  Rhat
## mu[1]     3600 0.999
## mu[2]     3530 1.002
## sigma[1]  3565 1.000
## sigma[2]  3600 1.001
## muDiff    3600 1.000
## Cohensd   3600 1.000
## HedgesG   3600 1.000
## nu        3472 1.000
## 
## Samples were drawn using NUTS(diag_e) at Sun Sep 17 12:34:30 2017.
## For each parameter, n_eff is a crude measure of effective sample size,
## and Rhat is the potential scale reduction factor on split chains (at 
## convergence, Rhat=1).

Chi tiết quan trọng nhất, đó là phân phối hậu định của muDiff (khác biệt trung bình) không chứa giá trị zero, và trung vị = 12.435, do đó ta có rất nhiều khả năng giả thuyết: SysBP thấp hơn ở nhóm tử vong là đúng. Hai giá trị effect-size theo Cohen và Hedges thấp hơn 1 chút so với t-test cổ điển, nhưng vẫn có thể diễn giải là “hiệu ứng mạnh”.

Ta có thể vẽ phân phối hậu nghiệm của MuDiff

muDiff = extract(fit, pars='muDiff',inc_warmup=F)$muDiff%>%as_tibble()

muDiff$group=factor(rep(c(1:100), e=3600/100))

muDiff%>%ggplot(aes(x=value))+ 
  geom_density(alpha=0.5,col="red4",size=1) +
  geom_point(y=0, alpha=.01, size=2,col="red3") +
  geom_line(aes(group=group, color=group), stat='density', alpha=.2,show.legend = F) +scale_x_continuous("Khác biệt trung bình")+
  theme_bw()

Tiếp theo, Nhi sẽ khảo sát phân phối hậu định của 200 giá trị dự báo của SysBP từ mô hình Bayes, ta muốn biết là prior có hợp lý không ? phân phối hậu định của kết quả có phù hợp với quan sát thực tế hay không ? (vì phân phối hậu định dựa vào prior). Kết quả cho thấy phân phối hậu định của Y (2 hình chuông màu xanh, đỏ) khá phù hợp với quan sát thực tế (2 đường cong màu đen)

resdf=extract(fit, pars='Res',inc_warmup=F)$Res%>%reshape2::melt(.)%>%as_tibble()
colnames(resdf) = c('iteration', 'case', 'SysBP' )
resdf$groupID=factor(rep(groupID, e=3600))

resdf$groupID=recode_factor(resdf$groupID,
                          `1`="Dead",
                          `2`="Survived")

resdf$case=as.factor(resdf$case)

resdf%>%ggplot(aes(x=SysBP)) + 
  geom_density(aes(group=groupID, fill=groupID), color=NA, alpha=.25) +
  geom_line(aes(group=case, col=groupID), stat='density', alpha=.05) +
  geom_point(aes(x=df$SysBP, y=0, color=factor(Outcome)), alpha=.15, size=2, data=df)+
  xlim(c(min(df$SysBP),max(df$SysBP)))+
  geom_density(aes(group=Outcome, linetype=Outcome, x=df$SysBP), alpha=.05, data=df)+
  theme_bw()
## Warning: Removed 4768 rows containing non-finite values (stat_density).

## Warning: Removed 4768 rows containing non-finite values (stat_density).

Nhi sẽ giới thiệu với các bạn cả 3 cách suy diễn thống kê theo Bayes, đó là Bayes Factor, Ngưỡng so sánh (CompVal) và khoảng vô nghĩa thực dụng (ROPE) :

p1=muDiff%>%mutate(.,Iteration=as.numeric(rownames(.)))%>%ggplot(aes(x=Iteration,y=value))+
  geom_path(col="orangered",alpha=0.7)+
  theme_bw()+ggtitle("Khác biệt trung bình")

p2=muDiff%>%ggplot(aes(x=value))+ 
  geom_density(alpha=0.5,fill="orangered",size=1) +
  scale_x_continuous("Khác biệt trung bình")+
  theme_bw()

cohend = extract(fit, pars='Cohensd',inc_warmup=F)$Cohensd%>%as_tibble()

p3=cohend%>%mutate(.,Iteration=as.numeric(rownames(.)))%>%
  ggplot(aes(x=Iteration,y=value))+
  geom_path(col="red",alpha=0.7)+
  theme_bw()+ggtitle("Effect-size")

p4=cohend%>%ggplot(aes(x=value))+ 
  geom_density(alpha=0.5,fill="red",size=1) +
  scale_x_continuous("Cohen's d")+
  theme_bw()

nu = extract(fit, pars='nu',inc_warmup=F)$nu%>%as_tibble()

p5=nu%>%mutate(.,Iteration=as.numeric(rownames(.)))%>%
  ggplot(aes(x=Iteration,y=value))+
  geom_path(col="purple",alpha=0.7)+
  theme_bw()+ggtitle("Nu")

p6=nu%>%ggplot(aes(x=value))+ 
  geom_density(alpha=0.5,fill="purple",size=1) +
  scale_x_continuous("Cohen's d")+
  theme_bw()

gridExtra::grid.arrange(p1,p2,p3,p4,p5,p6,ncol=2)

10 Suy diễn Bayes

Tiếp theo, Nhi sẽ giới thiệu với các bạn cả 3 cách suy diễn thống kê theo Bayes, đó là Bayes Factor, Ngưỡng so sánh (CompVal) và khoảng vô nghĩa thực dụng (ROPE) :

11 Bayes Factor

Bản chất của Bayes Factor là tỉ trọng chứng cứ ủng hộ cho một giả thuyết H1 so với giả thuyết H0. Giả thuyết này được xác định bằng 1 ngưỡng giá trị đặc biệt (thí dụ H1 : muDiff > 0, hoặc Cohen’s d > 0.4 chẳng hạn). Bayes factor cho phép kiểm chứng (xác nhận hay loại trừ) giả thuyết H1 có liên quan tới ngưỡng giá trị này.

Ranh giới của H0 và H1 là BF=1, BF càng cao (>1), thì độ khả tín của kết luận (H1) càng lớn, và ngược lại, BF càng thấp (<1) thì chứng cứ ủng hộ cho H0 càng cao. Thông thường khi BF > 10 đã là « strong evidence », BF >30 thì tỉ trọng chứng cứ đã là rất mạnh ; còn khi BF > 100 thì có thể gần như khẳng định chắc chắn H1 là đúng.

BF có thể tính trực tiếp nhờ hàm hypothesis trong package brms, nó nhận 1 input là mô hình STAN (bất kì mô hình Bayes nào do STAN tạo ra, dù là brms, rstan hay rstanarm), và xuất kết quả là Evidence ratio chính là BF.

Thí dụ: Ta muốn tính Bayes Factor cho giả thuyết H1: muDiff >3 so với giả thuyết H0: muDiff < hoặc = 3:

Kết quả cho ra BF = 15.29, tỉ trọng chứng cứ yếu

brms::hypothesis(fit,"muDiff>3",alpha=0.05)
## Hypothesis Tests for class :
##                  Estimate Est.Error l-95% CI u-95% CI Evid.Ratio Star
## (muDiff)-(3) > 0     9.33       5.9    -0.75      Inf      15.29     
## ---
## '*': The expected value under the hypothesis lies outside the 95% CI.

Bây giờ, ta muốn tính Bayes Factor cho giả thuyết H1: muDiff >0 so với giả thuyết H0: muDiff < hoặc =0:

Kết quả cho ra BF = 45.75, tỉ trọng chứng cứ cao hơn 30 nên mức độ xác tín cũng chắc chắn hơn

brms::hypothesis(fit,"muDiff>0",alpha=0.05)
## Hypothesis Tests for class :
##              Estimate Est.Error l-95% CI u-95% CI Evid.Ratio Star
## (muDiff) > 0    12.33       5.9     2.25      Inf      45.75    *
## ---
## '*': The expected value under the hypothesis lies outside the 95% CI.

Bạn có thể nêu ra cùng lúc hàng loạt cặp giả thuyết H0/H1, và biểu diễn cùng lúc nhiều giá trị BF tương ứng cho mỗi giả thuyết, như trong hình dưới đây :

threshold=rep(NA,21)
BayesFactor=rep(NA,21)

for(i in (1:21)){
  thr=i-1
  threshold[i]=thr
  hyp=paste("muDiff>",thr,sep="")
  bf=brms::hypothesis(fit,hyp,alpha=0.05)
  BayesFactor[i]=bf$hypothesis$Evid.Ratio
}

bfdf=cbind(threshold,BayesFactor)%>%as_tibble()

bfdf%>%ggplot(aes(x=threshold,y=BayesFactor,fill=BayesFactor))+
  geom_point(show.legend = F,size=5,shape=21,col="black")+
  geom_text(aes(label=round(BayesFactor,2)),col="black",show.legend = F,nudge_y = 5,nudge_x=1,angle = 45)+
  theme_bw()+
  geom_hline(yintercept = c(10,30),linetype=2,col="blue")+
  scale_fill_gradient(low="gold",high="red")

12 ROPE và CompVal

Ngoài BF, tác giả John Kruschke đề xuất diễn giải kết quả phân tích Bayes dựa vào tỉ trọng phân phối hậu nghiệm sau khi phân chia bằng 1 khoảng thực dụng (ROPE) hoặc 1 ngưỡng (CompVal). Mục tiêu là xác định mật độ (%) của phân phối hậu nghiệm nằm trong, hay ngoài (cao hơn, thấp hơn) ROPE, hoặc cao/thấp hơn ngưỡng CompVal.

Ngưỡng so sánh : CompVal có bản chất là 1 ngưỡng giá trị nhất định, cho phép chia phân phối hậu nghiệm thành 2 vùng: Cao hơn/Thấp hơn. Công dụng của nó là khẳng định 1 giả thuyết H1 hoặc loại trừ giả thuyết H0 có hàm ý so sánh với 1 ngưỡng ý nghĩa đặc biệt.

ROPE (Region of practice equivalence) có bản chất là 1 khoảng giá trị trên thang đo, giới hạn bởi 2 ngưỡng trên/dưới. ROPE cho phép chia phân phối hậu nghiệm ra 3 vùng:

Vùng trong ROPE xem như vô nghĩa (thí dụ: huyết áp giảm 0-3 mmHg được xem như không có ý nghĩa lâm sàng). Ngoài ROPE : gồm Vùng cao / Vùng Thấp

ROPE cho phép khẳng định 1 giả thuyết H1 hoặc loại trừ 1 giả thuyết H0 liên quan tới 1,2 hoặc 3 vùng với ý nghĩa thực dụng tùy chọn. Do đó ROPE tiện ích hơn nhiều so với BF và Null hypothesis testing, vì nó cho phép trả lời cả 3 câu hỏi : kết quả cao hay thấp so với ngưỡng so sánh ? kết quả có ý nghĩa lâm sàng hay không ? Mức độ khả tín là bao nhiêu ?

Phương pháp J.Kruschke áp dụng trên output thuộc lớp MCMC và dựa vào 1 vài hàm do tác giả viết, Nhi chỉ cải biên lại đôi chút.

HDIF= function( sampleVec,credMass=0.975 ) {
  sortedPts = sort( sampleVec )
  ciIdxInc = ceiling( credMass * length( sortedPts ) )
  nCIs = length( sortedPts ) - ciIdxInc
  ciWidth = rep( 0 , nCIs )
  for ( i in 1:nCIs ) {
    ciWidth[ i ] = sortedPts[ i + ciIdxInc ] - sortedPts[ i ]
  }
  HDImin = sortedPts[ which.min( ciWidth ) ]
  HDImax = sortedPts[ which.min( ciWidth ) + ciIdxInc ]
  HDIlim = c( HDImin , HDImax )
  return( HDIlim )
}

SUMK=function(paramSampleVec,compVal=NULL , ROPE=NULL , credMass=0.975) {
  meanParam = mean( paramSampleVec )
  medianParam = median( paramSampleVec )
  dres = density( paramSampleVec )
  modeParam = dres$x[which.max(dres$y)]
  hdiLim = HDIF( paramSampleVec , credMass=credMass )
  if ( !is.null(compVal) ) {
    pcgtCompVal = ( 100 * sum( paramSampleVec > compVal ) 
                    / length( paramSampleVec ) )
  } else {
    compVal=NA
    pcgtCompVal=NA
  }
  if ( !is.null(ROPE) ) {
    pcltRope = ( 100 * sum( paramSampleVec < ROPE[1] ) 
                 / length( paramSampleVec ) )
    pcgtRope = ( 100 * sum( paramSampleVec > ROPE[2] ) 
                 / length( paramSampleVec ) )
    pcinRope = 100-(pcltRope+pcgtRope)
  } else { 
    ROPE = c(NA,NA)
    pcltRope=NA 
    pcgtRope=NA 
    pcinRope=NA 
  }  
  return( c( Mean=meanParam , Median=medianParam , Mode=modeParam , 
             HDIlevel=credMass , LL=hdiLim[1] , UL=hdiLim[2] , 
             CompVal=compVal , PcntGtCompVal=pcgtCompVal , 
             ROPElow=ROPE[1] , ROPEhigh=ROPE[2] ,
             PcntLtROPE=pcltRope , PcntInROPE=pcinRope , PcntGtROPE=pcgtRope ) )
}

summaryKruschke=function(MCMC,compVal=NULL, rope=NULL,credMass=NULL){
  summaryInfo = NULL
  summaryInfo = cbind(summaryInfo, "Estimated"= SUMK(MCMC,
                                                     compVal=compVal,
                                                     ROPE=rope,credMass=credMass))
  return(summaryInfo)
}

summaryKruschke(MCMC = muDiff$value,compVal=0,rope=c(1,5),credMass=0.95)
##                Estimated
## Mean          12.3331004
## Median        12.4352939
## Mode          13.4199996
## HDIlevel       0.9500000
## LL             0.3912786
## UL            23.4514280
## CompVal        0.0000000
## PcntGtCompVal 97.8611111
## ROPElow        1.0000000
## ROPEhigh       5.0000000
## PcntLtROPE     3.2500000
## PcntInROPE     7.0277778
## PcntGtROPE    89.7222222

Trong thí dụ trên, Nhi đặt ngưỡng so sánh CompVal = 0.0, như vậy nó cho phép kiểm tra giả thuyết H0: muDiff < hay =0 và H1: muDiff >0; đồng thời Nhi đặt khoảng ROPE từ 0-5, như vậy giả thuyết ở đây là: nếu MuDiff rơi vào trong khoảng 0-5 (ROPE), xem như không có ý nghĩa lâm sàng, và 2 giả thuyết khác là: MuDiff <0 và MuDiff >5.

Kết quả cho thấy có 97.86 % mật độ phân phối hậu định của MuDiff nằm bên phải CompVal (>0), chỉ có 3.25 % mật độ phân phối hậu định MuDiff nằm ngoài và dưới ngưỡng 0 của ROPE, 7.03 % phân phối hậu định rơi vào bên trong ROPE,nhưng có đến 89.72 % mật độ nằm bên ngoài và cao hơn ngưỡng trên = 5 của ROPE.

13 Tổng kết

Bài thực hành đến đây là chấm dứt. Qua bài này, các bạn đã nắm được một quy trình hoàn chỉnh để thực hiện t-test BAYES, thay thế cho t-test cổ điển, bao gồm:

  • Cấu trúc chương trình và cú pháp của STAN code
  • Diễn đạt t-test theo ngôn ngữ mô hình
  • Cách chọn Prior
  • Khai thác phân phối hậu định
  • Suy diễn Bayes bằng 3 phương pháp

Hẹn gặp lại các bạn vào 1 dịp khác.

Nếu các bạn có hứng thú tham gia reboot cho dự án Bayes for Vietnam, xin liên lạc với nhóm chúng tôi.

Xin cảm ơn và hẹn gặp lại

LS0tDQp0aXRsZTogIktp4buDbSDEkeG7i25oIHQgdGhlbyBCYXllcyINCnN1YnRpdGxlOiAiU+G7rSBk4bulbmcgbmfDtG4gbmfhu68gU1RBTiINCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjE3IFRow6FuZyA5IDIwMTciDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyc3RhbikNCmxpYnJhcnkoYnJtcykNCg0KYGBgDQoNCiFbXShUdGVzdEJheWVzLnBuZykNCg0KIyBHaeG7m2kgdGhp4buHdSANCg0KVGjDom4gY2jDoG8gY8OhYyBi4bqhbiwgxJHDonkgbMOgIGLDoGkgdGjhu7FjIGjDoG5oIHRo4bupIGhhaSB0cm9uZyBk4buxIMOhbiBCYXllcyBmb3IgVmlldG5hbSwgxJHGsOG7o2MgcmVib290IGzhuqFpIHThu6sgcHJvamVjdCBjw7luZyB0w6puIG7Eg20gMjAxNi4gVHJvbmcgYsOgaSB0csaw4bubYywgTmhpIMSRw6MgZ2nhu5tpIHRoaeG7h3UgbeG7mXQgc+G7kSB0aMO0bmcgdGluIGPGoSBi4bqjbiB24buBIHRyxrDhu51uZyBwaMOhaSBCYXllcywgbeG7mXQgY8OhY2ggdGnhur9wIGPhuq1uIGtow6FjIGNobyBuaOG7r25nIHBow6JuIHTDrWNoIHbDoCBzdXkgZGnhu4VuIHRo4buRbmcga8OqLiBOZ3V5w6puIGzDvSBj4bunYSBwaMawxqFuZyBwaMOhcCBuw6B5IGThu7FhIHbDoG8gxJHhu4tuaCBsw70gQmF5ZXMsIMSRxrDhu6NjIGRp4buFbiDEkeG6oXQgYuG6sW5nIG5nw7RuIG5n4buvIG3DtCBow6xuaCwgeMOhYyBzdeG6pXQgxJFp4buBdSBraeG7h24uIFN1eSBkaeG7hW4gdGjhu5FuZyBrw6ogdGhlbyBCYXllcyBsacOqbiBr4bq/dCBnaeG7r2EgNCBwaOG6p246IEThu68gbGnhu4d1IHF1YW4gc8OhdCB0aOG7sWMgdOG6vywgZ2nhuqMgdGh1eeG6v3QgKHBow6JuIHBo4buRaSkgdGnhu4FuIMSR4buLbmggUHJpb3IsIGjDoG0gbGlrZWxpaG9vZCB2w6Aga+G6v3QgcXXhuqMgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmguDQoNCkLhuqNuIHRow6JuIHRyxrDhu51uZyBwaMOhaSBmcmVxdWVudGlzdCB2w6AgbnVsbCBoeXBvdGhlc2lzIHRlc3RpbmcgKE5IU1QpIGtow7RuZyBjw7MgZ8OsIHNhaSBj4bqjLCBuaMawbmcgbsOzIGtow7RuZyDEkeG7pyBraOG6oyBuxINuZyDEkeG7gyBnaeG6o2kgcXV54bq/dCBuaOG7r25nIGto4bunbmcgaG/huqNuZyB2w6AgYuG6pXQgY+G6rXAgdHJvbmcgc3V5IGRp4buFbiB0aOG7kW5nIGvDqiDhu6luZyBk4bulbmcgdsOgbyBuZ2hpw6puIGPhu6l1LiBLaMOhaSBuaeG7h20gw70gbmdoxKlhIHRo4buRbmcga8OqIHbDoCBuZ8aw4buhbmcgcF92YWx1ZSA8IDAuMDUgxJHDoyBzaW5oIHJhIG5oaeG7gXUgaOG7hyBxdeG6oyB0YWkgaOG6oWksIGfhu5NtIG5o4buvbmcgY8OhY2ggZ2lhbiBs4bqtbiBy4bqldCDEkcOhbmcgdHLDoWNoLCB0aMOtIGThu6UgOg0KDQorIELhu4thIMSR4bq3dCBnaeG6oyB0aHV54bq/dCBIMCB2w6AgSDEgU0FVIEtISSBuaMOsbiB0aOG6pXkgcF92YWx1ZQ0KDQorIFRo4buxYyBoaeG7h24gaMOgbmcgbG/huqF0IGtp4buDbSDEkeG7i25oIHRoZW8ga2nhu4N1IMSDbiBtYXkgKHRyYSB04bqlbiBk4buvIGxp4buHdSksIGThuqV1IGvhur90IHF14bqjIHA+MC4wNSB2w6Aga2hvZSBr4bq/dCBxdeG6oyBwPDAuMDUNCg0KKyBIYWNrIHRy4buLIHPhu5EgcCAodsOgIGPhuqMgQ0kpIMSR4buDIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ogYuG6sW5nIG3hu41pIGdpw6ENCg0KTmjGsCB24bqteSBOSFNUIGtow7RuZyBnaeG6o2kgcXV54bq/dCBt4buNaSB24bqlbiDEkeG7gSA6dGjDrSBk4bulICDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ogaG/DoG4gdG/DoG4ga2jDoWMgduG7m2kgw70gbmdoxKlhIGzDom0gc8OgbmcsIG3DoCBuw7MgY8OybiBnw6J5IHJhIHbhuqVuIMSR4bq/IG3hu5tpLiBOw7MgY8WpbmcgZ8OieSBuZ+G7mSBuaOG6rW4gdsOsIGtow7RuZyBjw7Mgbmhp4buBdSBuZ8aw4budaSBoaeG7g3UgYuG6o24gY2jhuqV0IHAtdmFsdWUgbMOgIGfDrC4gDQoNCkJheWVzIGPDsyAyIMawdSDEkWnhu4NtIG3DoCBraMO0bmcgdGjhu4MgY8OzIHRyb25nIHRyxrDhu51uZyBwaMOhaSBjxakgKGZyZXF1ZW50aXN0KSwgxJHDsyBsw6A6IA0KDQoxKSBIw7JhIGjhu6NwIGdp4buvYSBnaeG6oyDEkeG7i25oL2vhur90IHF14bqjIGPDsyB0csaw4bubYyB2w6AgZOG7ryBsaeG7h3UgdGjhu7FjIHThur8sDQoNCjIpIFLhuqV0IGxpbmggaG/huqF0OiBCYXllcyBraMO0bmcgY8OybiBi4buLIHLDoG5nIGJ14buZYyBi4bufaSBjw6FjIGdp4bqjIMSR4buLbmggxJFp4buBdSBraeG7h24gbmjGsCBwaMOibiBwaOG7kWkgY2h14bqpbiwgb3V0bGllcnMsIGPhu6EgbeG6q3UsIHBoxrDGoW5nIHNhaSDEkeG7k25nIG5o4bqldOKApjsgdsOgIGtoaSBjw7MgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggdHJvbmcgdGF5LCBjw7Mgbmhp4buBdSBjw6FjaCDEkeG7gyBkaeG7hW4gZ2nhuqNpLCBraeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dCBuZ2hpw6puIGPhu6l1IHbDoCBt4buXaSBjw6FjaCDEkeG7gXUga2jhuq9jIHBo4bulYyBjw6FjIGLhuqV0IGPhuq1wIGPhu6dhIHRy4buLIHPhu5EgcC4NCg0KIyBOZ8O0biBuZ+G7ryBTVEFODQoNCkPDsyBuaGnhu4F1IGPDoWNoIMSR4buDIHRo4buxYyBow6BuaCBCYXllcy4gxJDGoW4gZ2nhuqNuIG5o4bqldCBsw6AgZMO5bmcgbmjhu69uZyBjaMawxqFuZyB0csOsbmggKHBhY2thZ2UpIHZp4bq/dCBz4bq1biwgdGjDrSBk4bulIEJheWVzRmFjdG9yLCBNQ01DIGhv4bq3YyBjw6FjIHBhY2thZ2UgY2h1ecOqbiBiaeG7h3QgY2hvIHThu6tuZyBsb+G6oWkgbW9kZWwuIEPDoWMgY8O0bmcgY+G7pSBuw6B5IMSRxrBhIGLhuqFuIHRo4bqzbmcgdOG7m2kga+G6v3QgcXXhuqMgY3Xhu5FpIGPDuW5nIGzDoCBCYXllcyBmYWN0b3IgY+G7p2EgdGhhbSBz4buRLCBuaMawbmcga2jDtG5nIGNobyBiaeG6v3QgY8ahIGNo4bq/IGhv4bqhdCDEkeG7mW5nYsOqbiB0cm9uZyB2w6AgYuG6oW4gY8Wpbmcga2jDtG5nIHTDuXkgY2jhu4luaCDEkcaw4bujYyBjxqEgY2jhur8gbsOgeS4NCg0KUGjhu6ljIHThuqFwIGjGoW4sIGNow7puZyB0YSBjw7Mgbmjhu69uZyBuZ8O0biBuZ+G7ryBs4bqtcCB0csOsbmggY2hvIEdpYnMgc2FtcGxlciBuaMawIEJVR3MgdsOgIEpBR3MsIGNow7puZyDEkeG7gXUgY8OzIGdpYW8gdGjhu6ljIHRyb25nIFIgdsOgIGPDoWMgcGFja2FnZSDhu6luZyBk4bulbmcuIEN14buRaSBjw7luZywgY2jDum5nIHRhIGPDsyBuZ8O0biBuZ+G7ryBTVEFOIHbhu5tpIGPGoSBjaOG6vyBob8OgbiB0b8OgbiBraMOhYywgdsOgIFNUQU4gY8WpbmcgY8OzIGdpYW8gdGjhu6ljIHbDoCBwYWNrYWdlIOG7qW5nIGThu6VuZyB0cm9uZyBSIG5oxrAgcnN0YW4sIHJzdGFuYXJtLCBicm1z4oCmIA0KDQpOxINtIDIwMTMsIE5oaSBi4bqvdCDEkeG6p3UgaOG7jWMgQmF5ZXMsIHbhu6thIMSR4buNYyBsw70gdGh1eeG6v3QsIHbhu6thIHZp4bq/dCBjb2RlIGLhurFuZyBXaW5CVUdzLCBzYXUgxJHDsyBsw6AgSkFHcy4gQ3Xhu5FuIHPDoWNoIGPhu6dhIEogLiBLcnVzY2hrZSB2w6AgUmljaGFyZCBNY0VscmVhdGggxJHDoyBj4bqjaSDEkeG6oW8gY2hvIE5oaSB04burIHTDrW4gxJHhu5MgY+G7p2EgcGjDoWkgRnJlcXVlbnRpc3QgdGjDoG5oIHTDrW4gxJHhu5MgY+G7p2EgcGjDoWkgQmF5ZXMuIFNhdSDEkcOzIFNUQU4gcmEgxJHhu51pLCBjw7luZyB24bubaSAybmQgZWRpdGlvbiBj4bunYSBjdeG7kW4gc8OhY2ggY+G7p2EgS3J1c2Noa2UgdsOgIGjDoG5nIGxv4bqhdCBjw6FjIGdpYW8gdGjhu6ljIFNUQU4gdHJvbmcgUiDEkcOjIGNobyBwaMOpcCBOaGkgdGjhu7FjIGjDoG5oIEJheWVzIG3hu5l0IGPDoWNoIHRob+G6o2kgbcOhaSBoxqFuLiBEbyBTVEFOIGPDsyBuaGnhu4F1IMawdSDEkWnhu4NtIHbDoCDEkcaw4bujYyBo4buXIHRy4bujIMSR4bqneSDEkeG7pyBuw6puIGThu7Egw6FuIEJheWVzIGZvciBWaWV0bmFtIGNo4buNbiBTVEFOIGzDoG0gbmfDtG4gIG5n4buvIGNobyB0dXRvcmlhbC4NCg0KU1RBTiBsw6AgbeG7mXQgbmfDtG4gbmfhu68gbOG6rXAgdHLDrG5oIHRo4buRbmcga8OqIHjDoWMgc3XhuqV0LCDEkcaw4bujYyBwaMOhdCB0cmnhu4NuIGNobyBSLCBQeXRob24gdsOgIE1hdGxhYi5DxqEgY2jhur8gdOG6oW8gcmEgY2h14buXaSBNYXJrb3YgTW9udGUgQ2FybG8gY+G7p2EgU1RBTiBob8OgbiB0b8OgbiBraMOhYyBiaeG7h3QgduG7m2kgSkFHcyB2w6AgQlVHcyBsw6AgMiBzYW1wbGVyIGPDsyB0csaw4bubYy4gU1RBTiBz4butIGThu6VuZyBwaMawxqFuZyBwaMOhcCBIYW1pbHRvbmlhbiBNb250ZSBDYXJsbyAoSE1DKSBzYW1wbGluZyBk4buxYSB2w6BvIGzDvSB0aHV54bq/dCBj4bunYSBNZXRyb3BvbGlzLUhhc3RpbmdzICgxOTUzLCAxOTcwKS4gQuG6oW4gY8OzIHRo4buDIGluc3RhbGwgU1RBTiB04burIMSRw6J5Og0KDQpodHRwczovL2dpdGh1Yi5jb20vc3Rhbi1kZXYNCg0KIyBUIHRlc3QgDQoNCiFbXShzdHVkZW50dC5wbmcpDQoNCkvhu4MgdOG7qyBsw7pjIFdpbGxpYW0gU2VhbHkgR29zc2V0ICgxOTA4KSBwaMOhdCBtaW5oIHJhIGtp4buDbSDEkeG7i25oIHQgdsOgIHBow6JuIHBo4buRaSB0IGTGsOG7m2kgYsO6dCBkYW5oIGzDoCBTdHVkZW50LCB0IHRlc3QgxJHDoyB0cuG7nyB0aMOgbmggY8O0bmcgY+G7pSDEkcaw4bujYyBz4butIGThu6VuZyBwaOG7lSBiaeG6v24gbmjhuqV0IHRyb25nIGjhuqd1IGjhur90IGPDoWMgbmdoacOqbiBj4bupdSB5IGjhu41jLCBiYW8gZ+G7k20gbHXhuq1uIHbEg24gY+G7p2EgY8OhYyBzaW5oIHZpw6puLiBN4bulYyDEkcOtY2ggZ2nhuqNuIGThu4sgY+G7p2EgdCB0ZXN0IGzDoCDEkeG7gyBzbyBzw6FuaCBnacOhIHRy4buLIHRydW5nIGLDrG5oIGdp4buvYSAyIHBow6JuIG5ow7NtIGThu7FhIHbDoG8gbnVsbCBoeXBvdGhlc2lzIHRlc3RpbmcgdHLDqm4gdHLhu4sgc+G7kSB0LiBUaOG7sWMgcmEgdGVzdCB0IGNow61uaCBsw6AgbeG7mXQgbcO0IGjDrG5oIHR1eeG6v24gdMOtbmggbcO0IHThuqMgYmnhur9uIGvhur90IHF14bqjIFkgdGhlbyAyIGJp4bq/biBz4buRIGdp4bqjIGNo4buJIHBow6JuIG5ow7NtLiBUcm9uZyBSLCB0YSBjw7MgdGjhu4MgdGjhu7FjIGhp4buHbiB0ZXN0IHQgZOG7sWEgdsOgbyBjw7RuZyBj4bulIHRy4buxYyB0aeG6v3AgaG/hurdjIHF1YSBtw7QgaMOsbmggZ2xtLg0KDQpN4bulYyB0acOqdSBj4bunYSBiw6BpIHRo4buxYyBow6BuaCBuw6B5IGzDoCBnaeG7m2kgdGhp4buHdSBt4buZdCBjw6FjaCB0aeG6v3AgY+G6rW4gaG/DoG4gdG/DoG4gbeG7m2kgdGhlbyB0csaw4budbmcgcGjDoWkgQmF5ZXMgxJHhu4Mgc28gc8OhbmggMiBwaMOibiBuaMOzbSwgdGhheSB0aOG6vyBjaG8gdGVzdCB0LiANCg0KRG8gYsOgaSB0b8OhbiBuw6B5IHLhuqV0IHBo4buVIGJp4bq/biwgxJHDoyBjw7Mgbmhp4buBdSBjw7RuZyBj4bulIMSRw6MgxJHGsOG7o2MgdOG6oW8gcmEgdHJvbmcgUiBjaG8gcGjDqXAgbMOgbSB0cuG7sWMgdGnhur9wIHQtdGVzdCBCYXllcywgdGjDrSBk4bulIHBhY2thZ2UgQmF5ZXNGYWN0b3IsIEJFU1TigKYgVHV5IG5oacOqbiwgTmhpIHPhur0gZOG6q24gY8OhYyBi4bqhbiDEkWkgdGhlbyBjb24gxJHGsOG7nW5nIGTDoGkgaMahbiwgbMOgbSB0aOG7pyBjw7RuZyBt4buNaSB0aOG7qSB04burIGNvZGUgbcO0IGjDrG5oIGNobyDEkeG6v24gZGnhu4VuIGdp4bqjaSBr4bq/dCBxdeG6oywgbmjGsG5nIMSR4buVaSBs4bqhaSwgY8OhYyBi4bqhbiBz4bq9IGhp4buDdSByw7UgaMahbiB24buBIGPGoSBjaOG6vyBj4bunYSBtw7QgaMOsbmgsIGPhuqV1IHRyw7pjIFNUQU4gY29kZSwgduG7gSBwcmlvciwgdsOgIGtp4buDbSBzb8OhdCBt4buNaSB0aOG7qSwga+G7gyBj4bqjIHbhur0gYmnhu4N1IMSR4buTIHRoZW8gw70gdGjDrWNoLiANCg0KTmhpIGPFqW5nIHPhur0gZ2nhu5tpIHRoaeG7h3UgduG7m2kgY8OhYyBi4bqhbiBj4bqjIDMgY8OhY2ggc3V5IGRp4buFbiBCYXllcywgZMO5bmcgQmF5ZXMgRmFjdG9yLCBST1BFIHbDoCBuZ8aw4buhbmcgc28gc8OhbmguIEtoaSBr4bq/dCB0aMO6YyBiw6BpIHRo4buxYyBow6BuaCwgY8OhYyBi4bqhbiBz4bq9IGPDsyB0cm9uZyB0YXkgbeG7mXQgcXV5IHRyw6xuaCBob8OgbiBjaOG7iW5oIHRoYXkgdGjhur8gaG/DoG4gdG/DoG4gdCB0ZXN0IGPhu5UgxJFp4buDbiBi4bqxbmcgdCB0ZXN0IEJheWVzLg0KDQojIFThu6sgdCB0ZXN0IMSR4bq/biB0IHRlc3QgQmF5ZXMNCg0KTeG7mXQgY8OhY2ggdOG7lW5nIHF1w6F0LCBzdXkgZGnhu4VuIEJheWVzIGThu7FhIHRyw6puIMSR4buLbmggbMO9IEJheWVzIG5oxrAgc2F1IDoNCg0KJCQgcChcdGhldGEgfHkseClccHJvcHRvIHAoeXxcdGhldGEgLHgpIHAoXHRoZXRhICx4KSAkJA0KdGhlbyDEkcOzLCBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSBt4buZdCB0aGFtIHPhu5EgVGhldGEgKGtoaSB0YSBjw7MgdHJvbmcgdGF5IGThu68gbGnhu4d1IGJp4bq/biBr4bq/dCBxdeG6oyBZIHbDoCBo4bqxbmcgc+G7kSBYIGzDoCBt4buZdCBtYXRyaXggY8OhYyBwcmVkaWN0b3JzIHRyb25nIG3DtCBow6xuaCkgdOG7iSBs4buHIHbhu5tpIHTDrWNoIGPhu6dhIGjDoG0gbGlrZWxpaG9vZCAoeMOhYyBzdeG6pXQgY8OzIMSRaeG7gXUga2nhu4duIGPhu6dhIHkga2hpIGPDsyB0aGV0YSB2w6AgeCkgdsOgIHBow6JuIHBo4buRaSB0aeG7gW4gxJHhu4tuaCAocHJpb3I9IGdp4bqjIHRodXnhur90IHbhu4EgcGjDom4gcGjhu5FpIGPhu6dhIHRoZXRhKS4NCg0KTuG6v3UgY8OhYyBi4bqhbiB0aeG6v3AgY+G6rW4gxJHGsOG7o2MgduG7m2kgbmjhu69uZyB0w6BpIGxp4buHdSBUaOG7kW5nIGvDqiDEkeG7mWMgxJHDoW8sIG5oxrAgZ2nDoW8gdHLDrG5oIFNQU1MgdsOgIFIgY+G7p2EgQW5keSBGaWVsZCBoYXkgcXV54buDbiBTdGF0aXN0aWNhbCBSZXRoaW5raW5nIGPhu6dhIFJpY2hhcmQgTWNFbHJlYXRoLCBi4bqhbiBz4bq9IG5o4bqtbiByYSBy4bqxbmcgcXV5IHRyw6xuaCBzbyBzw6FuaCBt4buZdCBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyBnaeG7r2EgY8OhYyBwaMOibiBuaMOzbSB0aOG7sWMgY2jhuqV0IGzDoCBt4buZdCBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmguIFN1eSBy4buZbmcgcmEsIGfhuqduIG5oxrAgdOG6pXQgY+G6oyBuaOG7r25nIGPDonUgaOG7j2kgbmdoacOqbiBj4bupdSDEkeG7gXUgY8OzIHRo4buDIGRp4buFbiDEkeG6oXQgYuG6sW5nIG5nw7RuIG5n4buvIG3DtCBow6xuaC4gRG8gxJHDsywgaOG7jWMgduG7gSBtw7QgaMOsbmggbMOgIG3hu5l0IGPDoWNoIMSR4bqndSB0xrAga2jDtG4gbmdvYW4gdsOsIG7DsyB0cmFuZyBi4buLIGNobyBjw6FjIGLhuqFuIG3hu5l0IGPDtG5nIGPhu6UgcGjhu5UgcXXDoXQgxJHhu4MgdGnhur9wIGPhuq1uIHbhuqVuIMSR4buBLiANCg0KVHJvbmcgYsOgaSB0b8OhbiBzbyBzw6FuaCDEkeG6oWkgbMaw4bujbmcgWSBnaeG7r2EgY8OhYyBwaMOibiBuaMOzbSwgbcO0IGjDrG5oIGPhu6dhIGNow7puZyB0YSBz4bq9IGfhu5NtIG5o4buvbmcgeeG6v3UgdOG7kSBzYXUgxJHDonkgOg0KDQpN4buZdCDEkeG6oWkgbMaw4bujbmcgWSBsw6AgYmnhur9uIGxpw6puIHThu6VjIG3DoCB0YSBtdeG7kW4ga2jhuqNvIHPDoXQsIHRhIGfhu41pIG7DsyBsw6AgYmnhur9uIGvhur90IHF14bqjIChvdXRjb21lKS4gR2nDoSB0cuG7iyBvdXRjb21lIFkgbsOgeSDEkcaw4bujYyBtw7QgdOG6oyBi4bqxbmcgbeG7mXQga2nhu4N1IHBow6JuIHBo4buRaSB4w6FjIMSR4buLbmguDQoNClRoZW8gdGluaCB0aOG6p24gY+G7p2EgdCB0ZXN0IGPhu6dhIEdvc3NldCwgb3V0Y29tZSBsw6AgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBiaeG6v24gWSwgdsOgIGtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCBnaeG7r2EgMiBwaMOibiBuaMOzbSBHMSB2w6AgRzIuIEtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCBuw6B5IMSRxrDhu6NjIG3DtCB04bqjIGLhurFuZyAxIHBow6JuIHBo4buRaSB0LiBOaMawIHRhIGJp4bq/dCwgcGjDom4gcGjhu5FpIHQgY8OzIDMgdGhhbSBz4buRIGzDoCBtdSAobG9jYXRpb24pLCBzaWdtYSAoc2NhbGUpIHbDoCBraeG7g3UgaMOsbmggKGhheSDEkeG7mSB04buxIGRvKSBOdS4NCg0KTeG7pWMgdGnDqnUgY+G7p2EgdCB0ZXN0IEJheWVzIGzDoCB0w6xtIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIDMgdGhhbSBz4buRIG7DoHkuxJDhu4MgbMOgbSB2aeG7h2MgxJHDsywgdGEgcGjhuqNpIGThu7FuZyBtw7QgaMOsbmggdHJvbmcgU1RBTg0KDQojIEThu68gbGnhu4d1IG1pbmggaOG7jWENCg0KQsOgaSB0b8OhbiBtaW5oIGjhu41hIGPhu6dhIGNow7puZyB0YSBsw6AgZGF0YXNldCBJQ1UsIMSRw6J5IGzDoCBt4buZdCBuZ2hpw6puIGPhu6l1IG3DtCB04bqjIGPhuq90IG5nYW5nLCBraOG6o28gc8OhdCBodXnhur90IMOhcCB2w6AgbeG6oWNoIGPhu6dhIDIwMCBi4buHbmggbmjDom4gc2hvY2sgbmhp4buFbSBraHXhuqluIHRyb25nIGtob2EgSUNVLiBDw6J1IGjhu49pIG5naGnDqm4gY+G7qXUgZ2nhuqMgxJHhu4tuaCBj4bunYSBjaMO6bmcgdGEgbMOgIDogU28gc8OhbmggaHV54bq/dCDDoXAgdMOibSB0aHUgKFN5c0JQKSBnaeG7r2EgMiBwaMOibiBuaMOzbSBi4buHbmggbmjDom4gVOG7rSB2b25nIHbDoCBT4buRbmcgc8OzdC4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQojIFRoZSBEYXRhIGFuZCBTdG9yeSBMaWJyYXJ5IChEQVNMKSwgaHR0cDovL2xpYi5zdGF0LmNtdS5lZHUvREFTTC9EYXRhZmlsZXMvSUNVLmh0bWwNCg0KZGY9cmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1Yi5jb20vdmluY2VudGFyZWxidW5kb2NrL1JkYXRhc2V0cy9tYXN0ZXIvY3N2L1N0YXQyRGF0YS9JQ1UuY3N2IiklPiVhc190aWJibGUoKQ0KDQpkZiRPdXRjb21lPC1yZWNvZGVfZmFjdG9yKGRmJFN1cnZpdmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgYDBgPSJEZWFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBgMWA9IlN1cnZpdmVkIikNCg0KZGYkU3Vydml2ZTwtcmVjb2RlX2ZhY3RvcihkZiRTdXJ2aXZlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBgMGA9IjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBgMWA9IjIiKQ0KDQpwc3ljaDo6ZGVzY3JpYmVCeShkZiRTeXNCUCxkZiRTdXJ2aXZlKQ0KDQpkZiU+JWdncGxvdChhZXMoeD1TeXNCUCxmaWxsPU91dGNvbWUpKSsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPS42LGNvbD0iYmxhY2siKSsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4sY29sPU91dGNvbWUpLGFscGhhPTAuNSkrDQogIGZhY2V0X3dyYXAofk91dGNvbWUsbmNvbD0xLHNjYWxlcz0iZnJlZV95IikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIHQtdGVzdCB0aGVvIHRyxrDhu51uZyBwaMOhaSBmcmVxdWVudGlzdA0KDQrEkOG6p3UgdGnDqm4sIE5oaSBz4bq9IGzDoG0gdC10ZXN0IHRoZW8gdHLGsOG7nW5nIHBow6FpIGPhu5UgxJFp4buDbg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnQudGVzdChkZiRTeXNCUH5kZiRPdXRjb21lLGFsdGVybmF0aXZlPSJsZXNzIikNCg0KDQplZmZlY3RzaXplPWZ1bmN0aW9uKG91dGNvbWUsZmFjdG9yLGRhdGEpew0KICBkPWRhdGENCiAgbjI9SG1pc2M6OmRlc2NyaWJlKGRbZmFjdG9yXSklPiUuWzFdJT4lLltbZmFjdG9yXV0lPiUuJHZhbHVlcyU+JS4kZnJlcXVlbmN5JT4lLlsxXQ0KICBuMT1IbWlzYzo6ZGVzY3JpYmUoZFtmYWN0b3JdKSU+JS5bMV0lPiUuW1tmYWN0b3JdXSU+JS4kdmFsdWVzJT4lLiRmcmVxdWVuY3klPiUuWzJdDQogIHZhcj1kW291dGNvbWVdJT4lc3BsaXQoZFtmYWN0b3JdKSU+JW1hcCh+dmFyKC4pKQ0KICB2Mj12YXJbWzFdXSU+JS5bMV0NCiAgdjE9dmFyW1syXV0lPiUuWzFdDQogIG0yPWRbb3V0Y29tZV0lPiVzcGxpdChkW2ZhY3Rvcl0pJT4lbWFwKH5wc3ljaDo6ZGVzY3JpYmUoLikpJT4lLlsxXSU+JS5bWzFdXSU+JS4kbWVhbg0KICBtMT1kW291dGNvbWVdJT4lc3BsaXQoZFtmYWN0b3JdKSU+JW1hcCh+cHN5Y2g6OmRlc2NyaWJlKC4pKSU+JS5bMl0lPiUuW1sxXV0lPiUuJG1lYW4NCiAgcz1zcXJ0KCgobjEtMSkqdjErKG4yLTEpKnYyKS8objErbjItMikpDQogIGNvaGVuZD0oKG0xLW0yKS9zKQ0KICBoZWRnZUc9KChtMS1tMikvcykqKDEtICgzLyg0KihuMStuMiktOSkpKQ0KICBjYXQoIkVmZmVjdCBzaXplIGZvciBJbmRlcGVuZGVudCB0IHRlc3QiLCJcbiIpDQogIGNhdCgiQ29oZW4ncyBkIHZhbHVlPSIsY29oZW5kLCJcbiIpDQogIGNhdCgiSGVkZ2UncyBHPSIsaGVkZ2VHLCJcbiIpDQp9DQoNCmVmZmVjdHNpemUob3V0Y29tZT0iU3lzQlAiLGZhY3Rvcj0iT3V0Y29tZSIsZGF0YT1kZikNCg0KYGBgDQoNCkvhur90IHF14bqjIGNobyB0aOG6pXkgaHV54bq/dCDDoXAgZ2nhuqNtIG3hu5l0IGPDoWNoIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6og4bufIG5ow7NtIGLhu4duaCBuaMOibiB04butIHZvbmcgc28gduG7m2kgbmjDs20gc+G7kW5nIHPDs3QgKHQ9LTIuNDMsIHA9MC4wMDkzLCBDb2hlbidzIGQ9MC41MikuDQoNCiMgVmnhur90IGNvZGUgU1RBTiBjaG8gdC10ZXN0IEJBWUVTDQoNClRyxrDhu5tjIGtoaSB0aeG6v3AgY+G6rW4gZOG7ryBsaeG7h3UgKGRhdGEpLCB0YSDEkcOjIGPDsyB0aOG7gyDEkeG6t3QgZ2nhuqMgdGh1eeG6v3QgduG7gSBtw7QgaMOsbmguIFRy4bqhbmcgdGjDoWkgc8ahIGtoYWkgbsOgeSBj4bunYSBtw7QgaMOsbmggxJHGsOG7o2MgeMOhYyDEkeG7i25oIGLhurFuZyBjw6FjIHByaW9ycyAocGjDom4gcGjhu5FpIHRp4buBbiBuZ2hp4buHbSwgaGF5IHRp4buBbiDEkeG7i25oKSBj4bunYSBPdXRjb21lIHbDoCB04bqldCBj4bqjIGPDoWMgVGhhbSBz4buRIHRyb25nIG3DtCBow6xuaA0KDQpgYGB7cn0NCmxpYnJhcnkocnN0YW4pDQoNCnN0YW5tb2RlbGNvZGUgPSINCg0KZGF0YSB7DQppbnQ8bG93ZXI9MT4gTjsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ28gbWF1IChsb3dlcj0xOiBkZSBraWVtIHRyYSBudWxsIGRhdGEgPykNCmludDxsb3dlcj0yPiBOZzsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTbyBwaGFuIG5ob20gKGxvd2VyID0yOiBpdCBuaGF0IHBoYWkgY28gMiBwaGFuIG5ob20pDQpyZWFsPGxvd2VyPTU+IG4xOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENvIG1hdSBwaGFuIG5ob20gMTogaXQgbmhhdCBwaGFpIGNvIDUgY2FzZXMNCnJlYWw8bG93ZXI9NT4gbjI7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ28gbWF1IHBoYW4gbmhvbSAyOiBpdCBuaGF0IHBoYWkgY28gNSBjYXNlcw0KdmVjdG9yW05dIHk7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEJpZW4ga2V0IHF1YSBZIGxhIDEgdmVjdG9yIGNvIGRvIGRhaT1ODQppbnQ8bG93ZXI9MSwgdXBwZXI9Mj4gZ3JvdXBJRFtOXTsgICAgICAgICAgICAvLyAgZ3JvdXAgSUQgbGEgMSBzbyBuZ3V5ZW4gbmhhbiAyIGdpYSB0cmkgPTEgaG9hYyA9Mg0KfQ0KDQp0cmFuc2Zvcm1lZCBkYXRhew0KcmVhbCBtZWFueTsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEJpZW4gbWVhbnkgbGEgdHJ1bmcgYmluaCBjdWEgWSBsYSAxIHNvIHRodWM7IHByaW9yIGN1YSBubyBsYSBtdQ0KbWVhbnkgPSBtZWFuKHkpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFhhYyBkaW5oIGdpYSB0cmkgbWVhbnk9IGhhbSBtZWFuDQp9DQoNCnBhcmFtZXRlcnMgew0KdmVjdG9yWzJdIG11OyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEtoYWkgYmFvIHRoYW0gc28gY3VhIHBoYW4gcGhvaSBTdHVkZW50LXQ6IG11LCBzaWdtYSB2YSBudTsgbXUgbGEgMSB2ZWN0b3IgMiBnaWEgdHJpDQp2ZWN0b3I8bG93ZXI9MD5bMl0gc2lnbWE7ICAgICAgICAgICAgICAgICAgICAgLy8gc2lnbWEgbGEgMSB2ZWN0b3IsIGdpb2kgaGFuIGR1b2kgPTAsIG5oYW4gMiBnaWEgdHJpDQpyZWFsPGxvd2VyPTAsIHVwcGVyPTEwMD4gbnU7ICAgICAgICAgICAgICAgICAgLy8gbnUgbGEgZG8gdHUgZG8gaGF5IGtpZXUgaGluaCBjdWEgcGhhbiBwaG9pIHQsIGdpb2kgaGFuIDA6MTAwDQp9DQoNCnRyYW5zZm9ybWVkIHBhcmFtZXRlcnMgeyAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBoYW4gaG9hbiBjaHV5ZW4gdGhhbSBzbyBjdWEgbW8gaGluaCBzdGFuOiBraG9uZyBjYW4gdGhpZXQNCn0NCg0KbW9kZWwgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS2hhaSBiYW8gbm9pIGR1bmcgbW8gaGluaCANCi8vIGtoYWkgYmFvIHRpZW4gZGluaCAocHJpb3IpDQptdSB+IG5vcm1hbChtZWFueSwgMTApOyAgICAgICAgICAgICAgICAgICAgICAgLy8gVGllbiBkaW5oIChwcmlvcikgY3VhIHRoYW0gc28gbXUgbGEgcGhhbiBwaG9pIEdhdXNzaWFuLCB0Yj1tZWFueSwgc2Q9MTANCnNpZ21hIH4gY2F1Y2h5KDAsIDUpOyAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUaWVuIGRpbmggY3VhIHRoYW0gc28gU2lnbWEgbGEgcHAgQ2F1Y2h5KDAsNSkNCm51IH4gZXhwb25lbnRpYWwoMS4wLzI5KTsgICAgICAgICAgICAgICAgICAgICAvLyBUaWVuIGRpbmggY3VhIHRoYW0gc28gTnUgbGEgcHAgRXhwb25lbnRpYWwgdm9pIHJhdGU9MjkNCg0KDQovLyBraGFpIGJhbyBsaWtlbGlob29kID0gMSB2b25nIGxhcA0KDQpmb3IgKG4gaW4gMTpOKXsNCnlbbl0gfiBzdHVkZW50X3QobnUsIG11W2dyb3VwSURbbl1dLCBzaWdtYVtncm91cElEW25dXSk7DQp9DQoNCn0NCg0KZ2VuZXJhdGVkIHF1YW50aXRpZXMgeyAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRpbmggdG9hbiBjYWMgdGhhbSBzbyB0cm9uZyB0X3Rlc3QgDQp2ZWN0b3JbTl0gUmVzOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBQaGFuIHBob2kgaGF1IGRpbmggY3VhIHBoYW4gdmkgZ2lhIHRyaSBkdSBiYW8gWSB0aGVvIG1vIGhpbmgsIHlSZXAgbGEgMSB2ZWN0b3IgZG8gZGFpIE4NCnJlYWwgbXVEaWZmOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBLaGFjIGJpZXQgdHJ1bmcgYmluaCA6IG11RGlmZiBsYSAxIHNvIHRodWMNCnJlYWwgczsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBwb29sZWQgc2QNCnJlYWwgQ29oZW5zZDsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDb2hlbidzIGQgZWZmZWN0IHNpemUgKEphY29iIENvaGVuKSBsYSAxIHNvIHRodWMNCnJlYWwgSGVkZ2VzRzsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBIZWRnZXMncyBHIGVmZmVjdCBzaXplICggTGFycnkgSGVkZ2VzLCAxOTgxKSwgbGEgc28gdGh1Yw0KDQpmb3IgKG4gaW4gMTpOKXsNCiAgUmVzW25dID0gc3R1ZGVudF90X3JuZyhudSwgbXVbZ3JvdXBJRFtuXV0sIHNpZ21hW2dyb3VwSURbbl1dKTsNCn0NCg0KbXVEaWZmID0gbXVbMl0gLSBtdVsxXTsNCnM9c3FydCgoKG4xLTEpKnNpZ21hWzFdXjIrKG4yLTEpKnNpZ21hWzJdXjIpLyhuMStuMi0yKSk7ICAgICAgLy9UaW5oIHBvb2xlZCBzZA0KQ29oZW5zZCA9IG11RGlmZiAvIHM7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUaW5oIENvaGVuJ3MgZA0KSGVkZ2VzRyA9IChtdURpZmYgLyBzKSooMS0gKDMvKDQqKG4xK24yKS05KSkpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRpbmggSGVkZ2VzJ3MgRw0KfQ0KIg0KYGBgDQoNCk3hu5l0IGNoxrDGoW5nIHRyw6xuaCAocHJvZ3JhbSkgQmF5ZXMgdHJvbmcgbmfDtG4gbmfhu68gU1RBTiBjw7MgY+G6pXUgdHLDumMgbmhp4buBdSBwaOG6p24sIHbhu5tpIGRhbmggcGjDoXAgbmjGsCBzYXU6DQoNCjEpCVBo4bqnbiBkYXRhOiBjw7MgbuG7mWkgZHVuZyBsw6Aga2hhaSBiw6FvIGPDoWMgdGhhbSBz4buRIHbhu4EgZOG7ryBsaeG7h3UsIG3hu5dpIHRoYW0gc+G7kSBwaOG6o2kgxJHGsOG7o2MgxJHhu4tuaCBkYW5oLCB2w6AgxJHhu4tuaCBk4bqhbmcuIFRyb25nIHRow60gZOG7pSBuw6B5LCBjw7MgNiB0aGFtIHPhu5EgZGF0YSBsw6A6IGPhu6EgbeG6q3UgTiAobMOgIDEgc+G7kSBuZ3V5w6puID4xKSwgc+G7kSBwaMOibiBuaMOzbSBOZyAoc+G7kSBuZ3V5w6puIGTGsMahbmcgPiAyKSwgY+G7oSBt4bqrdSBt4buXaSBwaMOibiBuaMOzbSAobjEsbjIpIOKAkyBs4bq9IHJhIGPFqW5nIGzDoCBz4buRIG5ndXnDqm4sIG5oxrBuZyDEkcaw4bujYyDEkeG7i25oIGThuqFuZyBsw6Agc+G7kSB0aOG7sWMgKHJlYWwpLCB2w6wgTmhpIG114buRbiBkw7luZyBjaMO6bmcgxJHhu4MgdMOtbmggZWZmZWN0IHNpemUgKENvaGVu4oCZcyBkKSwgdsOgIHRyb25nIFNUQU4gY8OhYyBwaMOpcCB0w61uaCBz4buRIG5ndXnDqm4gc+G6vSBjaG8gcmEga+G6v3QgcXXhuqMgbMOgIHPhu5Egbmd1ecOqbiwgbMOgbSB0csOybiwgbMOgIMSRaeG7gXUgdGEga2jDtG5nIG1vbmcgbXXhu5FuLiBCaeG6v24ga+G6v3QgcXXhuqMgeSBsw6AgMSB2ZWN0b3Iga8OtY2ggdGjGsOG7m2MgTiwgVMOqbiBwaMOibiBuaMOzbSBncm91cElEIGzDoCAxIHPhu5Egbmd1ecOqbiwgbmjhuq1uIDIgZ2nDoSB0cuG7iyAxLCAyLg0KDQoNCjIpCVBo4bqnbiB0cmFuc2Zvcm1lZCBkYXRhIMSR4buDIHRo4buxYyBoaeG7h24gaG/DoW4gY2h1eeG7g24gZOG7ryBsaeG7h3UgKHRoxrDhu51uZyBsw6AgYmnhur9uIGvhur90IHF14bqjKSB0csaw4bubYyBraGkgxJHGsGEgdsOgbyBow6BtIGxpa2VsaWhvb2QuIFZp4buHYyBob8OhbiBjaHV54buDbiBuw6B5IGPDsyBi4bqjbiBjaOG6pXQgbMOgIMOhcCBk4bulbmcgMSBsaW5rIGZ1bmN0aW9uIGzDqm4gWS4gVHJvbmcgdGjDrSBk4bulIG7DoHksIG91dGNvbWUgY+G7p2EgbcO0IGjDrG5oIHQtdGVzdCBsw6AgMSBnacOhIHRy4buLIHRydW5nIGLDrG5oIGPhu6dhIFksIG7Dqm4gdGEga2hhaSBiw6FvIGJp4bq/biBvdXRjb21lIHNhdSBob8OhbiBjaHV54buDbiBsw6AgbWVhbnkgKDEgc+G7kSB0aOG7sWMpLCB2w6AgY8O0bmcgdGjhu6ljIGhvw6FuIGNodXnhu4NuIDogbWVhbiB5ID0gbWVhbih5KS4NCg0KDQozKQlQaOG6p24gdGhhbSBz4buRIChwYXJhbWV0ZXJzKTogxJDDonkgbMOgIG5o4buvbmcgdGhhbSBz4buRIG3DoCB0YSBj4bqnbiB0w6xtIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oLCBr4bq/dCBxdeG6oyBj4bunYSBtw7QgaMOsbmggQmF5ZXMuIFRyb25nIHRow60gZOG7pSBuw6B5LCB0YSBjw7MgMyB0aGFtIHPhu5EsIG11LCBzaWdtYSB2w6AgbnUgdMawxqFuZyDhu6luZyB24bubaSBsb2NhdGlvbiwgc2NhbGUgdsOgIHNoYXBlIGPhu6dhIG3hu5l0IHBow6JuIHBo4buRaSBTdHVkZW50IHQuIG11IGzDoCAxIHZlY3Rvciwgc2lnbWEgbMOgIDEgdmVjdG9yLCBjw7MgMiBuaMOhbmggY2hvIDIgcGjDom4gbmjDs20gKG5oxrAgduG6rXkga+G6v3QgcXXhuqMgc+G6vSBjw7MgbXUxLCBtdTIsIHNpZ21hMSwgc2lnbWEyKTsgbnUgbMOgIDEgc+G7kSB0aOG7sWMgZ2nhu5tpIGjhuqFuIHThu6sgMC0xMDAuDQoNCg0KNCkJUGjhuqduIHRoYW0gc+G7kSBob8OhbiBjaHV54buDbiAodHJhbnNmb3JtZWQgcGFyYW1ldGVycyk6IGtow7RuZyBkw7luZyDEkeG6v24g4bufIMSRw6J5IG7Dqm4gxJHGsOG7o2MgxJHhu4MgdHLhu5FuZw0KDQoNCjUpCVBo4bqnbiBtb2RlbCA6IG7hu5lpIGR1bmcgY+G7p2EgbcO0IGjDrG5oIEJheWVzOiDEkOG6p3UgdGnDqm4sIHRhIGtoYWkgYsOhbyBQcmlvcnMgY2hvIG3hu5dpIHRoYW0gc+G7kS4gTmjhuq9jIGzhuqFpIDogUHJpb3IgbMOgIG3hu5l0IGjDoG0gbeG6rXQgxJHhu5kgeMOhYyBzdeG6pXQgbcOgIHRhIGdp4bqjIMSR4buLbmggKHRpbikgcuG6sW5nIGjhu6NwIGzDvSBuaOG6pXQgY2hvIHRoYW0gc+G7kS4gUHJpb3IgbMOgIHRow7RuZyB0aW4gY8OzIHRyxrDhu5tjIGtoaSBuaMOsbiB0aOG6pXkga+G6v3QgcXXhuqMsIG7DsyBjw7MgdGjhu4MgbMOgIGvhur90IHF14bqjIHRyb25nIHF1w6Ega2jhu6kgKHkgdsSDbiksIGtpbmggbmdoaeG7h20gY8OhIG5ow6JuLCBob+G6t2MgZ2nhuqMgxJHhu4tuaCBsw70gdGh1eeG6v3QuIOG7niDEkcOieSwgdGEgbOG6p24gbMaw4bujdCDEkeG6t3QgMyBwcmlvciBjaG8gMyB0aGFtIHPhu5EgbXUsIHNpZ21hIHbDoCBudSBuaMawIHNhdToNCg0KTXUgbMOgIGxvY2F0aW9uIGPhu6dhIHBow6JuIHBo4buRaSB0LCBj4bunYSBiaeG6v24ga+G6v3QgcXXhuqMgTWVhbihZKS4gVsOsIG11IGzDoCAxIHPhu5EgdGjhu7FjLCB2w6Agc+G7sSBiaeG6v24gdGhpw6puIG5n4bqrdSBuaGnDqm4gY+G7p2EgbsOzIGPDsyBi4bqjbiBjaOG6pXQgbMOgIGvhur90IHF14bqjIGPhu6dhIG3hu5l0IHF1w6EgdHLDrG5oIHRow6ptL2Lhu5t0IG5o4buvbmcgY8OhIHRo4buDIGPDsyBnacOhIHRy4buLIHkgY2FvL3Ro4bqlcCBt4buZdCBjw6FjaCBuZ+G6q3Ugbmhpw6puLCBkbyDEkcOzIHBow6JuIHBo4buRaSBo4bujcCBsw70gbmjhuqV0IGPhu6dhIG11IHPhur0gbMOgIEdhdXNzaWFuIChub3JtYWwpLiBUaOG6rXQgduG6rXksIEdhdXNzaWFuIGzDoCBwaMOibiBwaOG7kWkgaOG7o3AgbMO9IG5o4bqldCBjaG8gMSBiaeG6v24gbGnDqm4gdOG7pWMga2hpIG3DoCB0YSBraMO0bmcgY8OzIGto4bqjIG7Eg25nIMSRxrBhIHJhIGLhuqV0IGPhu6kgZ2nhuqMgxJHhu4tuaCBuw6BvIGtow6FjLCBuw7MgbMOgIHF1eSBsdeG6rXQgY+G7p2EgbeG7jWkgaGnhu4duIHTGsOG7o25nIHNpbmggbMO9LCB04buxIG5oacOqbi4gUGjDom4gcGjhu5FpIEdhdXNzaWFuIChub3JtYWwpIG7DoHkgY8OzIGxvY2F0aW9uPSB0cnVuZyBiw6xuaCBj4bunYSB5IHbDoCBzY2FsZT0xMC4NCg0KUHJpb3IgY+G7p2Egc2lnbWEgdsOgIG51IGtow7MgZ2nhuqNpIHRow61jaCBoxqFuIChOaGkga2jDtG5nIGPDsyBnaeG6oyDEkeG7i25oIG7DoG8gbsOqbiBideG7mWMgcGjhuqNpIGTDuW5nIHBow6JuIHBo4buRaSBjYXVjaHkgY2hvIHNpZ21hIHbDoCBleHBvbmVudGlhbCB24bubaSByYXRlID0yOSBjaG8gbnUsIDIgcHJpb3IgbsOgeSDEkcOjIMSRxrDhu6NjIGtp4buDbSBjaOG7qW5nIHRyb25nIHBhY2thZ2UgQkVTVCB2w6AgdMOgaSBsaeG7h3UgY+G7p2EgS3J1c2Noa2UpLiDEkOG7gyBk4buFIGjDrG5oIGR1bmcsIHRhIGPDsyB0aOG7gyB0aOG7rSB24bq9IGRlbnNpdHkgcGxvdCBj4bunYSAyIHByaW9yIG7DoHkuIEdoaSBjaMO6IGzDoCBi4bqhbiBjw7MgdGjhu4MgZMO5bmcgcGjDom4gcGjhu5FpIEdhbW1hIHRoYXkgY2hvIGV4cG9uZW50aWFsIGPFqW5nIMSRxrDhu6NjLg0KDQpgYGB7cn0NCg0KcDE9cmV4cCg1MDAwLDEvMjkpJT4lDQogIGFzLmRhdGEuZnJhbWUoKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9LikpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGNvbD0iYmxhY2siLGZpbGw9InJlZCIpKw0KICB0aGVtZV9idygpK2dndGl0bGUoIlByaW9yIGNobyBudSIpDQoNCnAyPXJub3JtKDUwMDAsbWVhbihkZiRTeXNCUCksMTApJT4lDQogIGFzLmRhdGEuZnJhbWUoKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9LikpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGNvbD0iYmxhY2siLGZpbGw9InJlZCIpKw0KICB0aGVtZV9idygpK2dndGl0bGUoIlByaW9yIGNobyBNdSIpDQoNCnAzPXJjYXVjaHkoNTAwMCxsb2NhdGlvbiA9IDAsc2NhbGUgPTUpJT4lDQogIGFzLmRhdGEuZnJhbWUoKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9LikpK3hsaW0oYygwLG1heChkZiRTeXNCUCkpKSsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSxjb2w9ImJsYWNrIixmaWxsPSJyZWQiKSsNCiAgdGhlbWVfYncoKStnZ3RpdGxlKCJQcmlvciBjaG8gU2lnbWEiKQ0KDQpwND1kZiU+JWdncGxvdChhZXMoeD1TeXNCUCkpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGNvbD0iYmxhY2siLGZpbGw9InJlZCIpKw0KICB0aGVtZV9idygpK2dndGl0bGUoIlBow6JuIHBo4buRaSB0aOG7sWMgdOG6vyBj4bunYSBZIikNCg0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEscDIscDMscDQsbmNvbD0yKQ0KDQpgYGANCg0KIVtdKFByaW9ycy5wbmcpDQoNCk7hur91IGtow7RuZyBjw7MgcHJpb3IsIFNUQU4gc+G6vSBkw7luZyBmbGF0IHByaW9yLCBuaMawbmcga+G6v3QgcXXhuqMgc+G6vSBraMO0bmcgY2jDrW5oIHjDoWMNCg0KU2F1IGtoaSBraGFpIGLDoW8gcHJpb3IsIHRhIHZp4bq/dCAxIHbDsm5nIGzhurdwIGNobyBow6BtIGxpa2VsaWhvb2QgOiBu4buZaSBkdW5nIGPhu6dhIG7DsyBsw6AgxrDhu5tjIGzGsOG7o25nIGdpw6EgdHLhu4sgY+G7p2Egb3V0Y29tZSB5IHbhu5tpIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpICBzdHVkZW50LXQgxJHGsOG7o2MgeMOhYyDEkeG7i25oIGLhurFuZyAzIHRoYW0gc+G7kSBtdSwgc2lnbWEsIG51LCB0w7l5IHRoZW8gcGjDom4gbmjDs20gZ3JvdXBJRC4NCg0KxJDhur9uIMSRw6J5IGzDoCBo4bq/dCBu4buZaSBkdW5nIG1vZGVsLg0KDQo2KQlQaOG6p24gY3Xhu5FpIGPDuW5nIGPhu6dhIGNoxrDGoW5nIHRyw6xuaCBTVEFOIGPDsyB0w6puIGzDoCBnZW5lcmF0ZWQgcXVhbnRpdGllcywgxJHDonkgbMOgIG3hu5l0IGNo4bupYyBuxINuZyBy4bqldCBo4buvdSBk4bulbmcsIHbDrCBuw7MgY2hvIHBow6lwIGNow7puZyB0YSB04bqhbyByYSBi4bqldCBrw6wgdHLhu4sgc+G7kSB0aOG7kW5nIGvDqiBtw6AgdGEgdGjDrWNoIGThu7FhIHbDoG8gbcO0IGjDrG5oLiDEkMOieSBjaMOtbmggbMOgIGdpYW8gxJFp4buDbSBnaeG7r2EgdHLGsOG7nW5nIHBow6FpIEJheWVzIHbDoCB0aOG7kW5nIGvDqiBj4buVIMSRaeG7g24sIHRhIGPDsyB0aOG7gyByw7p0IHJhIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIG5o4buvbmcgdHLhu4sgc+G7kSB0aOG7kW5nIGvDqiBjaG8gdC10ZXN0IG3DoCB0YSB04burbmcgYmnhur90LCBuaMawIDoNCg0KUGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY+G7p2EgZ2nDoSB0cuG7iyBZIGNobyBt4buXaSBjw6EgdGjhu4MgdHJvbmcgbeG6q3UgOiBSZXMNCg0KUGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY2hvIGtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCA6IG11RGlmZiA9IG11IHBow6JuIG5ow7NtIDIg4oCTIG11IHBow6JuIG5ow7NtIDENCg0KUGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY2hvIEVmZmVjdCAtc2l6ZSBj4bunYSB0IHRlc3QgOiBDb2hlbuKAmXMgZCB2w6AgSGVkZ2XigJlzIEcNCg0KKG7hur91IHRow61jaCwgYuG6oW4gY8OzIHRo4buDIHTDrW5oIGPhuqMgcF92YWx1ZSBjaG8gMSBudWxsIGh5cG90aGVzaXMgdGVzdGluZzogdGjDrSBk4bulIEgxPW11RGlmZiA+IDAsIGNo4bqzbmcgaOG6oW4sIG5oxrBuZyBOaGkga2jDtG5nIG114buRbiDEkWkgbOG6oWMgcXXDoSB4YSB24buBIHBow6FpIGZyZXF1ZW50aXN0IOG7nyDEkcOieSkuDQoNCiMgVGhpIGjDoG5oIG3DtCBow6xuaCBTVEFODQoNClTDoWMgZ2nhuqMgSm9obiBLcnVzY2hrZSDEkcOjIGNoaWEgc+G6uyBraW5oIG5naGnhu4dtIGPhu6dhIMO0bmcga2hpIHZp4bq/dCBjb2RlIGNobyBtw7QgaMOsbmggU1RBTiA6IMSRaSBuZ8aw4bujYyB04burIGxpa2VsaWhvb2QgbW9kZWwgbMOqbiB0csOqbiwgdsOgIGRpIGNodXnhu4NuIGNvbiB0cuG7jyBjaHXhu5l0IGdp4buvYSBjw6FjIGJsb2NrLCDEkeG7gyDEkeG6o20gYuG6o28ga2jDtG5nIHPDs3QsIGtow7RuZyBzYWkgKGNo4buJIGPhuqduIHRoaeG6v3UgbeG7mXQgZOG6pXUgOyBoYXkgIHssIGNoxrDGoW5nIHRyw6xuaCBz4bq9IGtow7RuZyB0aOG7gyBjb21waWxlIMSRxrDhu6NjIHbDoCBiw6FvIGzhu5dpLCBkxKkgbmhpw6puIMSRw6J5IGzDoCDEkWnhu4F1IHThu5F0LCB2w6wgcXV5IHRyw6xuaCBzYW1wbGluZyBNQ01DIHLhuqV0IHThu5FuIGvDqW0gdGjhu51pIGdpYW4sIG7Dqm4gU1RBTiBraeG7g20gdHJhIGzhu5dpIGPDuiBwaMOhcCB0csaw4bubYyBraGkgdGhpIGjDoG5oLg0KDQpTYXUga2hpIMSRw6MgxJHDs25nIGfDs2kgbcO0IGjDrG5oLCB0YSBraGFpIGLDoW8gZGF0YSBjaG8gbcO0IGjDrG5oIHRyb25nIDEgbGlzdCwgxJHDonkgY2jDrW5oIGzDoCBjw6FjIHRoYW0gc+G7kSB0cm9uZyBibG9jayBkYXRhLiBUaMOtIGThu6Ug4bufIMSRw6J5IDogTj0yMDAsIE5nPTIsIG4xPTQwLCBuMj0xNjAsIGdyb3VwSUQgbMOgIG3hu5l0IHZlY3RvciBz4buRIG5ndXnDqm4gdOG7qyBjb2x1bW4gU3Vydml2ZSB0cm9uZyBkYXRhZnJhbWUsIHkgbMOgIHZlY3RvciBj4bunYSBjb2x1bW4gU3lzQlAgdHJvbmcgZGF0YSBmcmFtZS4NCg0KQsOieSBnaeG7nSB0YSDEkcawYSDEkW/huqFuIGNvZGUgdsOgIGRhdGEgdsOgbyBow6BtIHN0YW4gxJHhu4MgYuG6r3QgxJHhuqd1IHRoaSBow6BuaCBtw7QgaMOsbmguIFRyxrDhu5tjIHRpw6puLCBTVEFOIHPhur0ga2nhu4NtIHRyYSBjw7ogcGjDoXAgY+G7p2EgxJFv4bqhbiBjb2RlLCB0xrDGoW5nIGjhu6NwIGdp4buvYSBjw6FjIHRoYW0gc+G7kSB0cm9uZyDEkW/huqFuIGNvZGUgdsOgIGxpc3QgZGF0YS4gTuG6v3UgdOG6pXQgY+G6oyDEkeG7gXUgaOG7o3AgY2h14bqpbiwgxJFv4bqhbiBjb2RlIHbDoCBkYXRhIHPhur0gxJHGsOG7o2MgY29tcGlsZSB0aMOgbmggMSBwcm9ncmFtIEMrKywgdsOgIHRoaSBow6BuaC4gUHJvZ3JhbSBuw6B5IGvDrWNoIGhv4bqhdCBt4buZdCBzYW1wbGVyIMSR4buDIGLhuq90IMSR4bqndSBs4bqleSBt4bqrdSBjw6FjIGNodeG7l2kgTUNNQyB0aGVvIGPGoSBjaOG6vyBtw6AgdGEgxJHDoyDEkcawYSByYSwgdGjDrSBk4bulIDogMTAuMDAwIGzGsOG7o3QgaXRlcmF0aW9uLCBraOG7n2kgxJHhu5luZyAxMDAwIGzGsOG7o3QsIGNo4bqheSBzb25nIHNvbmcgdHLDqm4gNCBjb3JlcywgdsOgIHLDunQgZ+G7jW4gTUNNQyAodGhpbiA9MTApLiANCg0KYGBge3J9DQpncm91cElEPWFzLm51bWVyaWMoZGYkU3Vydml2ZSkNCg0KZGF0YT0gbGlzdChOPTIwMCwgTmc9MiwgbjE9NDAsIG4yPTE2MCwgZ3JvdXBJRD1ncm91cElELCB5PWRmJFN5c0JQKQ0KDQpzZXQuc2VlZCgxMjM0KQ0KDQpmaXQgPSBzdGFuKG1vZGVsX2NvZGU9c3Rhbm1vZGVsY29kZSwgZGF0YT1kYXRhLCBpdGVyPTEwMDAwLCB3YXJtdXA9MTAwMCwgY29yZXM9NCwgdGhpbj0xMCkNCg0KYGBgDQoNCk3DtCBow6xuaCBjaOG7iSBt4bqldCB2w6BpIHBow7p0IMSR4buDIGNvbnZlcmdlLCBi4bqhbiBz4bq9IGPDsyAxIG9iamVjdCBraG/huqNuZyA2LjUgTWIsIGLDqm4gdHJvbmcgbsOzIGzDoCBjw6FjIGNodeG7l2kgTUNNQyBjaG8gdOG6pXQgY+G6oyBuaOG7r25nIHRoYW0gc+G7kSwgdHLhu4sgc+G7kSBtw6AgYuG6oW4gxJHDoyBraGFpIGLDoW8uIEPhu6UgdGjhu4MsIG7DsyBjaOG7qWEgMjAwIGNodeG7l2kgTUNNQyBjaG8gZ2nDoSB0cuG7iyBk4buxIGLDoW8gU3lzQlAgY+G7p2EgdOG7q25nIGPDoSB0aOG7gyAoYmnhur9uIFJlcyksIDIgY2h14buXaSBNQ01DIGNobyBtdTEsIG11MiwgMiBjaHXhu5dpIGNobyBzaWdtYTEsIHNpZ21hMiwgMSBjaHXhu5dpIGNobyBtdURpZmYgKGvhur90IHF14bqjIGNow61uaCBj4bunYSB0IHRlc3QpLCAxIGNodeG7l2kgY2hvIENvaGVu4oCZcyBkIHbDoCAxIGNodeG7l2kgY2hvIEhlZGdl4oCZcyBHDQoNCiMgVGhhbSBkw7Iga+G6v3QgcXXhuqMgcGjDom4gcGjhu5FpIGjhuq11IG5naGnhu4dtDQoNCsSQ4bqndSB0acOqbiwgY2jDum5nIHRhIHhlbSBxdWEgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY+G7p2EgbXUsIHNpZ21hLCBtdURpZmYgKGvhur90IHF14bqjIGNow61uaCksIG51IHbDoCBlZmZlY3Qgc2l6ZSBDb2hlbidzIGQsIEhlZGdlcycgRw0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnByaW50KGZpdCwgZGlnaXRzPTMsIHBhcnM9YygnbXUnLCAnc2lnbWEnLCAnbXVEaWZmJywgJ0NvaGVuc2QnLCAnSGVkZ2VzRycsJ251JykpDQpgYGANCg0KQ2hpIHRp4bq/dCBxdWFuIHRy4buNbmcgbmjhuqV0LCDEkcOzIGzDoCBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSBtdURpZmYgKGtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCkga2jDtG5nIGNo4bupYSBnacOhIHRy4buLIHplcm8sIHbDoCB0cnVuZyB24buLID0gMTIuNDM1LCBkbyDEkcOzIHRhIGPDsyBy4bqldCBuaGnhu4F1IGto4bqjIG7Eg25nIGdp4bqjIHRodXnhur90OiBTeXNCUCB0aOG6pXAgaMahbiDhu58gbmjDs20gdOG7rSB2b25nIGzDoCDEkcO6bmcuIEhhaSBnacOhIHRy4buLIGVmZmVjdC1zaXplIHRoZW8gQ29oZW4gdsOgIEhlZGdlcyB0aOG6pXAgaMahbiAxIGNow7p0IHNvIHbhu5tpIHQtdGVzdCBj4buVIMSRaeG7g24sIG5oxrBuZyB24bqrbiBjw7MgdGjhu4MgZGnhu4VuIGdp4bqjaSBsw6AgImhp4buHdSDhu6luZyBt4bqhbmgiLg0KDQpUYSBjw7MgdGjhu4MgduG6vSBwaMOibiBwaOG7kWkgaOG6rXUgbmdoaeG7h20gY+G7p2EgTXVEaWZmDQoNCmBgYHtyfQ0KbXVEaWZmID0gZXh0cmFjdChmaXQsIHBhcnM9J211RGlmZicsaW5jX3dhcm11cD1GKSRtdURpZmYlPiVhc190aWJibGUoKQ0KDQptdURpZmYkZ3JvdXA9ZmFjdG9yKHJlcChjKDE6MTAwKSwgZT0zNjAwLzEwMCkpDQoNCm11RGlmZiU+JWdncGxvdChhZXMoeD12YWx1ZSkpKyANCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSxjb2w9InJlZDQiLHNpemU9MSkgKw0KICBnZW9tX3BvaW50KHk9MCwgYWxwaGE9LjAxLCBzaXplPTIsY29sPSJyZWQzIikgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPWdyb3VwLCBjb2xvcj1ncm91cCksIHN0YXQ9J2RlbnNpdHknLCBhbHBoYT0uMixzaG93LmxlZ2VuZCA9IEYpICtzY2FsZV94X2NvbnRpbnVvdXMoIktow6FjIGJp4buHdCB0cnVuZyBiw6xuaCIpKw0KICB0aGVtZV9idygpDQpgYGANCg0KVGnhur9wIHRoZW8sIE5oaSBz4bq9IGto4bqjbyBzw6F0IHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIDIwMCBnacOhIHRy4buLIGThu7EgYsOhbyBj4bunYSBTeXNCUCB04burIG3DtCBow6xuaCBCYXllcywgdGEgbXXhu5FuIGJp4bq/dCBsw6AgcHJpb3IgY8OzIGjhu6NwIGzDvSBraMO0bmcgPyBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSBr4bq/dCBxdeG6oyBjw7MgcGjDuSBo4bujcCB24bubaSBxdWFuIHPDoXQgdGjhu7FjIHThur8gaGF5IGtow7RuZyA/ICh2w6wgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggZOG7sWEgdsOgbyBwcmlvcikuIEvhur90IHF14bqjIGNobyB0aOG6pXkgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY+G7p2EgWSAoMiBow6xuaCBjaHXDtG5nIG3DoHUgeGFuaCwgxJHhu48pIGtow6EgcGjDuSBo4bujcCB24bubaSBxdWFuIHPDoXQgdGjhu7FjIHThur8gKDIgxJHGsOG7nW5nIGNvbmcgbcOgdSDEkWVuKQ0KDQpgYGB7cn0NCnJlc2RmPWV4dHJhY3QoZml0LCBwYXJzPSdSZXMnLGluY193YXJtdXA9RikkUmVzJT4lcmVzaGFwZTI6Om1lbHQoLiklPiVhc190aWJibGUoKQ0KY29sbmFtZXMocmVzZGYpID0gYygnaXRlcmF0aW9uJywgJ2Nhc2UnLCAnU3lzQlAnICkNCnJlc2RmJGdyb3VwSUQ9ZmFjdG9yKHJlcChncm91cElELCBlPTM2MDApKQ0KDQpyZXNkZiRncm91cElEPXJlY29kZV9mYWN0b3IocmVzZGYkZ3JvdXBJRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYDFgPSJEZWFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYDJgPSJTdXJ2aXZlZCIpDQoNCnJlc2RmJGNhc2U9YXMuZmFjdG9yKHJlc2RmJGNhc2UpDQoNCnJlc2RmJT4lZ2dwbG90KGFlcyh4PVN5c0JQKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGFlcyhncm91cD1ncm91cElELCBmaWxsPWdyb3VwSUQpLCBjb2xvcj1OQSwgYWxwaGE9LjI1KSArDQogIGdlb21fbGluZShhZXMoZ3JvdXA9Y2FzZSwgY29sPWdyb3VwSUQpLCBzdGF0PSdkZW5zaXR5JywgYWxwaGE9LjA1KSArDQogIGdlb21fcG9pbnQoYWVzKHg9ZGYkU3lzQlAsIHk9MCwgY29sb3I9ZmFjdG9yKE91dGNvbWUpKSwgYWxwaGE9LjE1LCBzaXplPTIsIGRhdGE9ZGYpKw0KICB4bGltKGMobWluKGRmJFN5c0JQKSxtYXgoZGYkU3lzQlApKSkrDQogIGdlb21fZGVuc2l0eShhZXMoZ3JvdXA9T3V0Y29tZSwgbGluZXR5cGU9T3V0Y29tZSwgeD1kZiRTeXNCUCksIGFscGhhPS4wNSwgZGF0YT1kZikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpOaGkgc+G6vSBnaeG7m2kgdGhp4buHdSB24bubaSBjw6FjIGLhuqFuIGPhuqMgMyBjw6FjaCBzdXkgZGnhu4VuIHRo4buRbmcga8OqIHRoZW8gQmF5ZXMsIMSRw7MgbMOgIEJheWVzIEZhY3RvciwgTmfGsOG7oW5nIHNvIHPDoW5oIChDb21wVmFsKSB2w6Aga2hv4bqjbmcgdsO0IG5naMSpYSB0aOG7sWMgZOG7pW5nIChST1BFKSA6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpwMT1tdURpZmYlPiVtdXRhdGUoLixJdGVyYXRpb249YXMubnVtZXJpYyhyb3duYW1lcyguKSkpJT4lZ2dwbG90KGFlcyh4PUl0ZXJhdGlvbix5PXZhbHVlKSkrDQogIGdlb21fcGF0aChjb2w9Im9yYW5nZXJlZCIsYWxwaGE9MC43KSsNCiAgdGhlbWVfYncoKStnZ3RpdGxlKCJLaMOhYyBiaeG7h3QgdHJ1bmcgYsOsbmgiKQ0KDQpwMj1tdURpZmYlPiVnZ3Bsb3QoYWVzKHg9dmFsdWUpKSsgDQogIGdlb21fZGVuc2l0eShhbHBoYT0wLjUsZmlsbD0ib3JhbmdlcmVkIixzaXplPTEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKCJLaMOhYyBiaeG7h3QgdHJ1bmcgYsOsbmgiKSsNCiAgdGhlbWVfYncoKQ0KDQpjb2hlbmQgPSBleHRyYWN0KGZpdCwgcGFycz0nQ29oZW5zZCcsaW5jX3dhcm11cD1GKSRDb2hlbnNkJT4lYXNfdGliYmxlKCkNCg0KcDM9Y29oZW5kJT4lbXV0YXRlKC4sSXRlcmF0aW9uPWFzLm51bWVyaWMocm93bmFtZXMoLikpKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9SXRlcmF0aW9uLHk9dmFsdWUpKSsNCiAgZ2VvbV9wYXRoKGNvbD0icmVkIixhbHBoYT0wLjcpKw0KICB0aGVtZV9idygpK2dndGl0bGUoIkVmZmVjdC1zaXplIikNCg0KcDQ9Y29oZW5kJT4lZ2dwbG90KGFlcyh4PXZhbHVlKSkrIA0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGZpbGw9InJlZCIsc2l6ZT0xKSArDQogIHNjYWxlX3hfY29udGludW91cygiQ29oZW4ncyBkIikrDQogIHRoZW1lX2J3KCkNCg0KbnUgPSBleHRyYWN0KGZpdCwgcGFycz0nbnUnLGluY193YXJtdXA9RikkbnUlPiVhc190aWJibGUoKQ0KDQpwNT1udSU+JW11dGF0ZSguLEl0ZXJhdGlvbj1hcy5udW1lcmljKHJvd25hbWVzKC4pKSklPiUNCiAgZ2dwbG90KGFlcyh4PUl0ZXJhdGlvbix5PXZhbHVlKSkrDQogIGdlb21fcGF0aChjb2w9InB1cnBsZSIsYWxwaGE9MC43KSsNCiAgdGhlbWVfYncoKStnZ3RpdGxlKCJOdSIpDQoNCnA2PW51JT4lZ2dwbG90KGFlcyh4PXZhbHVlKSkrIA0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41LGZpbGw9InB1cnBsZSIsc2l6ZT0xKSArDQogIHNjYWxlX3hfY29udGludW91cygiQ29oZW4ncyBkIikrDQogIHRoZW1lX2J3KCkNCg0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEscDIscDMscDQscDUscDYsbmNvbD0yKQ0KDQpgYGANCg0KIyBTdXkgZGnhu4VuIEJheWVzDQoNClRp4bq/cCB0aGVvLCBOaGkgc+G6vSBnaeG7m2kgdGhp4buHdSB24bubaSBjw6FjIGLhuqFuIGPhuqMgMyBjw6FjaCBzdXkgZGnhu4VuIHRo4buRbmcga8OqIHRoZW8gQmF5ZXMsIMSRw7MgbMOgIEJheWVzIEZhY3RvciwgTmfGsOG7oW5nIHNvIHPDoW5oIChDb21wVmFsKSB2w6Aga2hv4bqjbmcgdsO0IG5naMSpYSB0aOG7sWMgZOG7pW5nIChST1BFKSA6DQoNCiFbXShCYXllc2lhbmluZmVyZW5jZS5wbmcpDQoNCiMgQmF5ZXMgRmFjdG9yDQoNCkLhuqNuIGNo4bqldCBj4bunYSBCYXllcyBGYWN0b3IgbMOgIHThu4kgdHLhu41uZyBjaOG7qW5nIGPhu6kg4bunbmcgaOG7mSBjaG8gbeG7mXQgZ2nhuqMgdGh1eeG6v3QgSDEgc28gduG7m2kgZ2nhuqMgdGh1eeG6v3QgSDAuIEdp4bqjIHRodXnhur90IG7DoHkgxJHGsOG7o2MgeMOhYyDEkeG7i25oIGLhurFuZyAxIG5nxrDhu6FuZyBnacOhIHRy4buLIMSR4bq3YyBiaeG7h3QgKHRow60gZOG7pSBIMSA6IG11RGlmZiA+IDAsIGhv4bq3YyBDb2hlbuKAmXMgZCA+IDAuNCBjaOG6s25nIGjhuqFuKS4gQmF5ZXMgZmFjdG9yIGNobyBwaMOpcCBraeG7g20gY2jhu6luZyAoeMOhYyBuaOG6rW4gaGF5IGxv4bqhaSB0cuG7qykgZ2nhuqMgdGh1eeG6v3QgSDEgY8OzIGxpw6puIHF1YW4gdOG7m2kgbmfGsOG7oW5nIGdpw6EgdHLhu4sgbsOgeS4gDQoNClJhbmggZ2nhu5tpIGPhu6dhIEgwIHbDoCBIMSBsw6AgQkY9MSwgQkYgY8OgbmcgY2FvICg+MSksIHRow6wgxJHhu5kga2jhuqMgdMOtbiBj4bunYSBr4bq/dCBsdeG6rW4gKEgxKSBjw6BuZyBs4bubbiwgdsOgIG5nxrDhu6NjIGzhuqFpLCBCRiBjw6BuZyB0aOG6pXAgKDwxKSB0aMOsIGNo4bupbmcgY+G7qSDhu6duZyBo4buZIGNobyBIMCBjw6BuZyBjYW8uIFRow7RuZyB0aMaw4budbmcga2hpIEJGID4gMTAgxJHDoyBsw6Agwqsgc3Ryb25nIGV2aWRlbmNlIMK7LCBCRiA+MzAgdGjDrCB04buJIHRy4buNbmcgY2jhu6luZyBj4bupIMSRw6MgbMOgIHLhuqV0IG3huqFuaCA7IGPDsm4ga2hpIEJGID4gMTAwIHRow6wgY8OzIHRo4buDIGfhuqduIG5oxrAga2jhurNuZyDEkeG7i25oIGNo4bqvYyBjaOG6r24gSDEgbMOgIMSRw7puZy4NCg0KQkYgY8OzIHRo4buDIHTDrW5oIHRy4buxYyB0aeG6v3Agbmjhu50gaMOgbSBoeXBvdGhlc2lzIHRyb25nIHBhY2thZ2UgYnJtcywgbsOzIG5o4bqtbiAxIGlucHV0IGzDoCBtw7QgaMOsbmggU1RBTiAoYuG6pXQga8OsIG3DtCBow6xuaCBCYXllcyBuw6BvIGRvIFNUQU4gdOG6oW8gcmEsIGTDuSBsw6AgYnJtcywgcnN0YW4gaGF5IHJzdGFuYXJtKSwgdsOgIHh14bqldCBr4bq/dCBxdeG6oyBsw6AgRXZpZGVuY2UgcmF0aW8gY2jDrW5oIGzDoCBCRi4NCg0KVGjDrSBk4bulOiBUYSBtdeG7kW4gdMOtbmggQmF5ZXMgRmFjdG9yIGNobyBnaeG6oyB0aHV54bq/dCBIMTogbXVEaWZmID4zIHNvIHbhu5tpIGdp4bqjIHRodXnhur90IEgwOiBtdURpZmYgPCBob+G6t2MgPSAzOg0KDQpL4bq/dCBxdeG6oyBjaG8gcmEgQkYgPSAxNS4yOSwgdOG7iSB0cuG7jW5nIGNo4bupbmcgY+G7qSB54bq/dQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCg0KYnJtczo6aHlwb3RoZXNpcyhmaXQsIm11RGlmZj4zIixhbHBoYT0wLjA1KQ0KDQpgYGANCg0KQsOieSBnaeG7nSwgdGEgbXXhu5FuIHTDrW5oIEJheWVzIEZhY3RvciBjaG8gZ2nhuqMgdGh1eeG6v3QgSDE6IG11RGlmZiA+MCBzbyB24bubaSBnaeG6oyB0aHV54bq/dCBIMDogbXVEaWZmIDwgaG/hurdjID0wOg0KDQpL4bq/dCBxdeG6oyBjaG8gcmEgQkYgPSA0NS43NSwgdOG7iSB0cuG7jW5nIGNo4bupbmcgY+G7qSBjYW8gaMahbiAzMCBuw6puIG3hu6ljIMSR4buZIHjDoWMgdMOtbiBjxaluZyBjaOG6r2MgY2jhuq9uIGjGoW4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmJybXM6Omh5cG90aGVzaXMoZml0LCJtdURpZmY+MCIsYWxwaGE9MC4wNSkNCg0KYGBgDQoNCkLhuqFuIGPDsyB0aOG7gyBuw6p1IHJhIGPDuW5nIGzDumMgaMOgbmcgbG/huqF0IGPhurdwIGdp4bqjIHRodXnhur90IEgwL0gxLCB2w6AgYmnhu4N1IGRp4buFbiBjw7luZyBsw7pjIG5oaeG7gXUgZ2nDoSB0cuG7iyBCRiB0xrDGoW5nIOG7qW5nIGNobyBt4buXaSBnaeG6oyB0aHV54bq/dCwgbmjGsCB0cm9uZyBow6xuaCBkxrDhu5tpIMSRw6J5IDoNCg0KYGBge3J9DQp0aHJlc2hvbGQ9cmVwKE5BLDIxKQ0KQmF5ZXNGYWN0b3I9cmVwKE5BLDIxKQ0KDQpmb3IoaSBpbiAoMToyMSkpew0KICB0aHI9aS0xDQogIHRocmVzaG9sZFtpXT10aHINCiAgaHlwPXBhc3RlKCJtdURpZmY+Iix0aHIsc2VwPSIiKQ0KICBiZj1icm1zOjpoeXBvdGhlc2lzKGZpdCxoeXAsYWxwaGE9MC4wNSkNCiAgQmF5ZXNGYWN0b3JbaV09YmYkaHlwb3RoZXNpcyRFdmlkLlJhdGlvDQp9DQoNCmJmZGY9Y2JpbmQodGhyZXNob2xkLEJheWVzRmFjdG9yKSU+JWFzX3RpYmJsZSgpDQoNCmJmZGYlPiVnZ3Bsb3QoYWVzKHg9dGhyZXNob2xkLHk9QmF5ZXNGYWN0b3IsZmlsbD1CYXllc0ZhY3RvcikpKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRixzaXplPTUsc2hhcGU9MjEsY29sPSJibGFjayIpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPXJvdW5kKEJheWVzRmFjdG9yLDIpKSxjb2w9ImJsYWNrIixzaG93LmxlZ2VuZCA9IEYsbnVkZ2VfeSA9IDUsbnVkZ2VfeD0xLGFuZ2xlID0gNDUpKw0KICB0aGVtZV9idygpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKDEwLDMwKSxsaW5ldHlwZT0yLGNvbD0iYmx1ZSIpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0iZ29sZCIsaGlnaD0icmVkIikNCg0KDQpgYGANCg0KIyBST1BFIHbDoCBDb21wVmFsDQoNCk5nb8OgaSBCRiwgdMOhYyBnaeG6oyBKb2huIEtydXNjaGtlIMSR4buBIHh14bqldCBkaeG7hW4gZ2nhuqNpIGvhur90IHF14bqjIHBow6JuIHTDrWNoIEJheWVzIGThu7FhIHbDoG8gdOG7iSB0cuG7jW5nIHBow6JuIHBo4buRaSBo4bqtdSBuZ2hp4buHbSBzYXUga2hpIHBow6JuIGNoaWEgYuG6sW5nIDEga2hv4bqjbmcgdGjhu7FjIGThu6VuZyAoUk9QRSkgaG/hurdjIDEgbmfGsOG7oW5nIChDb21wVmFsKS4gTeG7pWMgdGnDqnUgbMOgIHjDoWMgxJHhu4tuaCBt4bqtdCDEkeG7mSAoJSkgY+G7p2EgcGjDom4gcGjhu5FpIGjhuq11IG5naGnhu4dtIG7hurFtIHRyb25nLCBoYXkgbmdvw6BpIChjYW8gaMahbiwgdGjhuqVwIGjGoW4pIFJPUEUsIGhv4bq3YyBjYW8vdGjhuqVwIGjGoW4gbmfGsOG7oW5nIENvbXBWYWwuDQoNCk5nxrDhu6FuZyBzbyBzw6FuaCA6IENvbXBWYWwgY8OzIGLhuqNuIGNo4bqldCBsw6AgMSBuZ8aw4buhbmcgZ2nDoSB0cuG7iyBuaOG6pXQgxJHhu4tuaCwgY2hvIHBow6lwIGNoaWEgcGjDom4gcGjhu5FpIGjhuq11IG5naGnhu4dtIHRow6BuaCAyIHbDuW5nOiBDYW8gaMahbi9UaOG6pXAgaMahbi4gQ8O0bmcgZOG7pW5nIGPhu6dhIG7DsyBsw6Aga2jhurNuZyDEkeG7i25oIDEgZ2nhuqMgdGh1eeG6v3QgSDEgaG/hurdjIGxv4bqhaSB0cuG7qyBnaeG6oyB0aHV54bq/dCBIMCBjw7MgaMOgbSDDvSBzbyBzw6FuaCB24bubaSAxIG5nxrDhu6FuZyDDvSBuZ2jEqWEgxJHhurdjIGJp4buHdC4gDQoNClJPUEUgKFJlZ2lvbiBvZiBwcmFjdGljZSBlcXVpdmFsZW5jZSkgY8OzIGLhuqNuIGNo4bqldCBsw6AgMSBraG/huqNuZyBnacOhIHRy4buLIHRyw6puIHRoYW5nIMSRbywgZ2nhu5tpIGjhuqFuIGLhu59pIDIgbmfGsOG7oW5nIHRyw6puL2TGsOG7m2kuIFJPUEUgY2hvIHBow6lwIGNoaWEgcGjDom4gcGjhu5FpIGjhuq11IG5naGnhu4dtIHJhIDMgdsO5bmc6DQoNClbDuW5nIHRyb25nIFJPUEUgeGVtIG5oxrAgdsO0IG5naMSpYSAodGjDrSBk4bulOiBodXnhur90IMOhcCBnaeG6o20gMC0zIG1tSGcgxJHGsOG7o2MgeGVtIG5oxrAga2jDtG5nIGPDsyDDvSBuZ2jEqWEgbMOibSBzw6BuZykuDQpOZ2/DoGkgUk9QRSA6IGfhu5NtIFbDuW5nIGNhbyAvIFbDuW5nIFRo4bqlcA0KDQpST1BFIGNobyBwaMOpcCBraOG6s25nIMSR4buLbmggMSBnaeG6oyB0aHV54bq/dCBIMSBob+G6t2MgbG/huqFpIHRy4burIDEgZ2nhuqMgdGh1eeG6v3QgSDAgbGnDqm4gcXVhbiB04bubaSAxLDIgaG/hurdjIDMgdsO5bmcgduG7m2kgw70gbmdoxKlhIHRo4buxYyBk4bulbmcgdMO5eSBjaOG7jW4uIERvIMSRw7MgUk9QRSB0aeG7h24gw61jaCBoxqFuIG5oaeG7gXUgc28gduG7m2kgQkYgdsOgIE51bGwgaHlwb3RoZXNpcyB0ZXN0aW5nLCB2w6wgbsOzIGNobyBwaMOpcCB0cuG6oyBs4budaSBj4bqjIDMgY8OidSBo4buPaSA6IGvhur90IHF14bqjIGNhbyBoYXkgdGjhuqVwIHNvIHbhu5tpIG5nxrDhu6FuZyBzbyBzw6FuaCA/IGvhur90IHF14bqjIGPDsyDDvSBuZ2jEqWEgbMOibSBzw6BuZyBoYXkga2jDtG5nID8gTeG7qWMgxJHhu5kga2jhuqMgdMOtbiBsw6AgYmFvIG5oacOqdSA/DQoNClBoxrDGoW5nIHBow6FwIEouS3J1c2Noa2Ugw6FwIGThu6VuZyB0csOqbiBvdXRwdXQgdGh14buZYyBs4bubcCBNQ01DIHbDoCBk4buxYSB2w6BvIDEgdsOgaSBow6BtIGRvIHTDoWMgZ2nhuqMgdmnhur90LCBOaGkgY2jhu4kgY+G6o2kgYmnDqm4gbOG6oWkgxJHDtGkgY2jDunQuDQoNCmBgYHtyfQ0KSERJRj0gZnVuY3Rpb24oIHNhbXBsZVZlYyxjcmVkTWFzcz0wLjk3NSApIHsNCiAgc29ydGVkUHRzID0gc29ydCggc2FtcGxlVmVjICkNCiAgY2lJZHhJbmMgPSBjZWlsaW5nKCBjcmVkTWFzcyAqIGxlbmd0aCggc29ydGVkUHRzICkgKQ0KICBuQ0lzID0gbGVuZ3RoKCBzb3J0ZWRQdHMgKSAtIGNpSWR4SW5jDQogIGNpV2lkdGggPSByZXAoIDAgLCBuQ0lzICkNCiAgZm9yICggaSBpbiAxOm5DSXMgKSB7DQogICAgY2lXaWR0aFsgaSBdID0gc29ydGVkUHRzWyBpICsgY2lJZHhJbmMgXSAtIHNvcnRlZFB0c1sgaSBdDQogIH0NCiAgSERJbWluID0gc29ydGVkUHRzWyB3aGljaC5taW4oIGNpV2lkdGggKSBdDQogIEhESW1heCA9IHNvcnRlZFB0c1sgd2hpY2gubWluKCBjaVdpZHRoICkgKyBjaUlkeEluYyBdDQogIEhESWxpbSA9IGMoIEhESW1pbiAsIEhESW1heCApDQogIHJldHVybiggSERJbGltICkNCn0NCg0KU1VNSz1mdW5jdGlvbihwYXJhbVNhbXBsZVZlYyxjb21wVmFsPU5VTEwgLCBST1BFPU5VTEwgLCBjcmVkTWFzcz0wLjk3NSkgew0KICBtZWFuUGFyYW0gPSBtZWFuKCBwYXJhbVNhbXBsZVZlYyApDQogIG1lZGlhblBhcmFtID0gbWVkaWFuKCBwYXJhbVNhbXBsZVZlYyApDQogIGRyZXMgPSBkZW5zaXR5KCBwYXJhbVNhbXBsZVZlYyApDQogIG1vZGVQYXJhbSA9IGRyZXMkeFt3aGljaC5tYXgoZHJlcyR5KV0NCiAgaGRpTGltID0gSERJRiggcGFyYW1TYW1wbGVWZWMgLCBjcmVkTWFzcz1jcmVkTWFzcyApDQogIGlmICggIWlzLm51bGwoY29tcFZhbCkgKSB7DQogICAgcGNndENvbXBWYWwgPSAoIDEwMCAqIHN1bSggcGFyYW1TYW1wbGVWZWMgPiBjb21wVmFsICkgDQogICAgICAgICAgICAgICAgICAgIC8gbGVuZ3RoKCBwYXJhbVNhbXBsZVZlYyApICkNCiAgfSBlbHNlIHsNCiAgICBjb21wVmFsPU5BDQogICAgcGNndENvbXBWYWw9TkENCiAgfQ0KICBpZiAoICFpcy5udWxsKFJPUEUpICkgew0KICAgIHBjbHRSb3BlID0gKCAxMDAgKiBzdW0oIHBhcmFtU2FtcGxlVmVjIDwgUk9QRVsxXSApIA0KICAgICAgICAgICAgICAgICAvIGxlbmd0aCggcGFyYW1TYW1wbGVWZWMgKSApDQogICAgcGNndFJvcGUgPSAoIDEwMCAqIHN1bSggcGFyYW1TYW1wbGVWZWMgPiBST1BFWzJdICkgDQogICAgICAgICAgICAgICAgIC8gbGVuZ3RoKCBwYXJhbVNhbXBsZVZlYyApICkNCiAgICBwY2luUm9wZSA9IDEwMC0ocGNsdFJvcGUrcGNndFJvcGUpDQogIH0gZWxzZSB7IA0KICAgIFJPUEUgPSBjKE5BLE5BKQ0KICAgIHBjbHRSb3BlPU5BIA0KICAgIHBjZ3RSb3BlPU5BIA0KICAgIHBjaW5Sb3BlPU5BIA0KICB9ICANCiAgcmV0dXJuKCBjKCBNZWFuPW1lYW5QYXJhbSAsIE1lZGlhbj1tZWRpYW5QYXJhbSAsIE1vZGU9bW9kZVBhcmFtICwgDQogICAgICAgICAgICAgSERJbGV2ZWw9Y3JlZE1hc3MgLCBMTD1oZGlMaW1bMV0gLCBVTD1oZGlMaW1bMl0gLCANCiAgICAgICAgICAgICBDb21wVmFsPWNvbXBWYWwgLCBQY250R3RDb21wVmFsPXBjZ3RDb21wVmFsICwgDQogICAgICAgICAgICAgUk9QRWxvdz1ST1BFWzFdICwgUk9QRWhpZ2g9Uk9QRVsyXSAsDQogICAgICAgICAgICAgUGNudEx0Uk9QRT1wY2x0Um9wZSAsIFBjbnRJblJPUEU9cGNpblJvcGUgLCBQY250R3RST1BFPXBjZ3RSb3BlICkgKQ0KfQ0KDQpzdW1tYXJ5S3J1c2Noa2U9ZnVuY3Rpb24oTUNNQyxjb21wVmFsPU5VTEwsIHJvcGU9TlVMTCxjcmVkTWFzcz1OVUxMKXsNCiAgc3VtbWFyeUluZm8gPSBOVUxMDQogIHN1bW1hcnlJbmZvID0gY2JpbmQoc3VtbWFyeUluZm8sICJFc3RpbWF0ZWQiPSBTVU1LKE1DTUMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBWYWw9Y29tcFZhbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk9QRT1yb3BlLGNyZWRNYXNzPWNyZWRNYXNzKSkNCiAgcmV0dXJuKHN1bW1hcnlJbmZvKQ0KfQ0KDQpzdW1tYXJ5S3J1c2Noa2UoTUNNQyA9IG11RGlmZiR2YWx1ZSxjb21wVmFsPTAscm9wZT1jKDEsNSksY3JlZE1hc3M9MC45NSkNCg0KYGBgDQoNClRyb25nIHRow60gZOG7pSB0csOqbiwgTmhpIMSR4bq3dCBuZ8aw4buhbmcgc28gc8OhbmggQ29tcFZhbCA9IDAuMCwgbmjGsCB24bqteSBuw7MgY2hvIHBow6lwIGtp4buDbSB0cmEgZ2nhuqMgdGh1eeG6v3QgSDA6IG11RGlmZiA8IGhheSA9MCB2w6AgSDE6IG11RGlmZiA+MDsgxJHhu5NuZyB0aOG7nWkgTmhpIMSR4bq3dCBraG/huqNuZyBST1BFIHThu6sgMC01LCBuaMawIHbhuq15IGdp4bqjIHRodXnhur90IOG7nyDEkcOieSBsw6A6IG7hur91IE11RGlmZiByxqFpIHbDoG8gdHJvbmcga2hv4bqjbmcgMC01IChST1BFKSwgeGVtIG5oxrAga2jDtG5nIGPDsyDDvSBuZ2jEqWEgbMOibSBzw6BuZywgdsOgIDIgZ2nhuqMgdGh1eeG6v3Qga2jDoWMgbMOgOiBNdURpZmYgPDAgdsOgIE11RGlmZiA+NS4NCg0KS+G6v3QgcXXhuqMgY2hvIHRo4bqleSBjw7MgOTcuODYgJSBt4bqtdCDEkeG7mSBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSBNdURpZmYgbuG6sW0gYsOqbiBwaOG6o2kgQ29tcFZhbCAoPjApLCBjaOG7iSBjw7MgMy4yNSAlIG3huq10IMSR4buZIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIE11RGlmZiBu4bqxbSBuZ2/DoGkgdsOgIGTGsOG7m2kgbmfGsOG7oW5nIDAgY+G7p2EgUk9QRSwgNy4wMyAlIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIHLGoWkgdsOgbyBiw6puIHRyb25nIFJPUEUsbmjGsG5nIGPDsyDEkeG6v24gODkuNzIgJSBt4bqtdCDEkeG7mSBu4bqxbSBiw6puIG5nb8OgaSB2w6AgY2FvIGjGoW4gbmfGsOG7oW5nIHRyw6puID0gNSBj4bunYSBST1BFLg0KDQojIFThu5VuZyBr4bq/dA0KDQpCw6BpIHRo4buxYyBow6BuaCDEkeG6v24gxJHDonkgbMOgIGNo4bqlbSBk4bupdC4gUXVhIGLDoGkgbsOgeSwgY8OhYyBi4bqhbiDEkcOjIG7huq9tIMSRxrDhu6NjIG3hu5l0IHF1eSB0csOsbmggaG/DoG4gY2jhu4luaCDEkeG7gyB0aOG7sWMgaGnhu4duIHQtdGVzdCBCQVlFUywgdGhheSB0aOG6vyBjaG8gdC10ZXN0IGPhu5UgxJFp4buDbiwgYmFvIGfhu5NtOg0KDQorIEPhuqV1IHRyw7pjIGNoxrDGoW5nIHRyw6xuaCB2w6AgY8O6IHBow6FwIGPhu6dhIFNUQU4gY29kZQ0KKyBEaeG7hW4gxJHhuqF0IHQtdGVzdCB0aGVvIG5nw7RuIG5n4buvIG3DtCBow6xuaA0KKyBDw6FjaCBjaOG7jW4gUHJpb3INCisgS2hhaSB0aMOhYyBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaA0KKyBTdXkgZGnhu4VuIEJheWVzIGLhurFuZyAzIHBoxrDGoW5nIHBow6FwDQoNCkjhurluIGfhurdwIGzhuqFpIGPDoWMgYuG6oW4gdsOgbyAxIGThu4twIGtow6FjLg0KDQpO4bq/dSBjw6FjIGLhuqFuIGPDsyBo4bupbmcgdGjDuiB0aGFtIGdpYSByZWJvb3QgY2hvIGThu7Egw6FuIEJheWVzIGZvciBWaWV0bmFtLCB4aW4gbGnDqm4gbOG6oWMgduG7m2kgbmjDs20gY2jDum5nIHTDtGkuIA0KDQoqWGluIGPhuqNtIMahbiB2w6AgaOG6uW4gZ+G6t3AgbOG6oWkq