Ghi chú:

Bs. Lê Ngọc Khả Nhi là thành viên trong Coreteam của nhóm BAV (Bayesian analysis for Vietnam). Trong bài viết có sử dụng 1 số ý tưởng, R function và code của anh Nhật Nam là team leader. Cảm ơn bạn Đinh Tiến Tài trong việc kiểm tra STAN code.

1 Giới thiệu

Thân chào các bạn, đây là bài thực hành thứ 6 trong dự án Bayes for Vietnam. Mục tiêu của chúng tôi là phổ cập về phương pháp thống kê theo trường phái Bayes nhằm thay thế hoàn toàn những công cụ truyền thống. Nối tiếp bài trước với chủ đề ANOVA đơn biến, bài này sẽ giới thiệu với các bạn thiết kế Thí nghiệm lặp lại (Repeated measure) và cách phân tích dữ liệu loại này. Nhi giả định là các bạn đã quen thuộc với cơ chế của suy diễn Bayes, cấu trúc ngôn ngữ STAN và quy trình khai thác phân phối hậu định, nên một số phần sẽ được giản lược.

Trong bài này chúng ta sẽ thay thế phân tích phương sai cho thí nghiệm lặp lại (Repeated measure ANOVA) bằng mô hình hỗn hợp và hồi quy Bayes. Như thường lệ, bài giảng sẽ đi theo 3 bước như sau:

Trước hết, chúng tôi sẽ ôn lại lý thuyết về ANOVA repeated measure, đưa ra 1 bài toán và minh họa quy trình ANOVA cổ điển theo phái frequentist.

Sau đó chúng tôi sẽ chuyển bài toán này thành mô hình Bayes, và hướng dẫn các bạn viết STAN code cho mô hình.

Cuối cùng, chúng ta sẽ khai thác phân phối hậu định cho các tham số cần quan tâm, và suy diễn Bayes.

2 Ôn tập về ANOVA thí nghiệm lặp lại

Bài toán minh họa

Dữ liệu minh họa trong bài này là một thí nghiệm sinh lý trên động vật vào năm 1994 của GS. J. Ludbrook. Trong thí nghiệm nguyên thủy, mục tiêu nhằm khảo sát hiệu ứng tương tác của hoạt chất phenylbiguanide (kích thích co mạch) và 1 chất đối vận serotonin (có tác dụng giảm co mạch) gây thay đổi huyết áp ở 5 con thỏ. Để đơn giản vấn đề, chúng tôi loại bỏ yếu tố can thiệp với đối vận serotonin, và chỉ giữ lại nhóm chứng và hiệu ứng của phenylbiguanide.

Câu hỏi nghiên cứu giả định trong bài toán hiện tại, do đó sẽ là: khảo sát hiệu ứng của liều thuốc phenylbiguanide lên huyết áp của thỏ.

Quy trình thí nghiệm là như sau: 5 con thỏ sẽ được tiêm tĩnh mạch lần lượt 6 liều phenylbiguanide từ D1 (thấp nhất) đến D6 (cao nhất) với khoảng cách 10 phút. Huyết áp thỏ được đo cùng lúc (như vậy mỗi con thỏ được đo huyết áp 6 lần).

library(tidyverse)

df=read.csv("https://raw.github.com/vincentarelbundock/Rdatasets/master/csv/MASS/Rabbit.csv")

# Within only

df$step=rep(c("D1","D2","D3","D4","D5","D6"),5)

df=subset(df,Treatment=="Control")%>%.[,c(2:3,6,7)]%>%as_tibble()%>%mutate(logDose=log(.$Dose))

head(df)%>%knitr::kable()
BPchange Dose Animal step logDose
0.5 6.25 R1 D1 1.832582
4.5 12.50 R1 D2 2.525729
10.0 25.00 R1 D3 3.218876
26.0 50.00 R1 D4 3.912023
37.0 100.00 R1 D5 4.605170
32.0 200.00 R1 D6 5.298317

Bảng thống kê mô tả:

# Descriptive analysis

d=psych::describeBy(df$BPchange,df$step)

t1=rbind(d$D1[,c(2:13)], 
         d$D2[,c(2:13)], 
         d$D3[,c(2:13)], 
         d$D4[,c(2:13)],
         d$D5[,c(2:13)],
         d$D6[,c(2:13)])%>%as.data.frame()%>%round(.,2)

row.names(t1)=c("D1","D2","D3","D4","D5","D6")

knitr::kable(t1)
n mean sd median trimmed mad min max range skew kurtosis se
D1 5 1.00 0.40 1.0 1.00 0.37 0.50 1.5 1.00 0.00 -1.91 0.18
D2 5 2.35 1.39 1.5 2.35 0.37 1.25 4.5 3.25 0.57 -1.70 0.62
D3 5 5.60 2.70 5.0 5.60 1.48 3.00 10.0 7.00 0.64 -1.40 1.21
D4 5 17.40 5.46 16.0 17.40 4.45 12.00 26.0 14.00 0.54 -1.54 2.44
D5 5 27.80 7.19 27.0 27.80 8.90 20.00 37.0 17.00 0.13 -2.05 3.22
D6 5 27.20 6.22 29.0 27.20 5.93 18.00 33.0 15.00 -0.42 -1.81 2.78

Bây giờ chúng ta sẽ phân tích bài toán này.

Kết quả là BPchange, 1 biến định lượng liên tục. Mỗi con thỏ đều tham gia vào cả 6 lần đo tương ứng D1 đến D6. Khảo sát trực quan cho thấy khuynh hướng chung là BPchange tăng dần từ D1 đến D6 với dao động ngẫu nhiên tùy theo cá thể thỏ;

# Exploration

library(viridis)
## Loading required package: viridisLite
df%>%ggplot(aes(x=step,y=BPchange,fill=Animal,group=1))+
  geom_line(aes(color=Animal),size=1)+
  geom_point(shape=21,size=3,col="black")+
  theme_bw()+
  scale_color_viridis(discrete=T,option="C",direction=-1)+
  scale_fill_viridis(discrete=T,option="C",direction=-1)+
  facet_wrap(~Animal,scale="free",ncol=2)

Thí nghiệm lặp lại là một thiết kế nghiên cứu trong đó kết quả định lượng Yij sẽ được khảo sát nhiều lần tại nhiều thời điểm (Tj) khác nhau hoặc dưới những điều kiện/phương pháp (Fj) khác nhau tên cùng đối tượng (Si).

Thiết kế này có rất nhiều ứng dụng trong nghiên cứu y học, thí dụ:

  • Đánh giá hiệu quả điều trị của phương pháp phẫu thuật: mỗi bệnh nhân được khảo sát 1 chỉ số lâm sàng Y trước và sau khi phẫu thuật (lưu ý: Repeated measure ANOVA là dạng tổng quát của paired samples Student t-test).

  • Khảo sát tính tương hợp của 3 máy đo đường huyết: Cùng một bệnh nhân sẽ được đo đường huyết bởi 3 máy đo, theo thứ tự ngẫu nhiên. Một thiết kế tương tự: khảo sát sự tương hợp chẩn đoán của 3 bác sĩ chẩn đoán hình ảnh khi cho họ đọc kết quả của cùng 1 phim CT-scan.

  • Nghiên cứu theo dõi kéo dài (longitudinal study): Một nhóm bệnh nhân được theo dõi diễn tiến của marker lâm sàng Y mỗi tháng một lần trong suốt quá trình điều trị bằng loại thuốc X, trong thời gian 1 năm.

Có 2 chi tiết quan trọng cần lưu ý:

  1. Cần phân biệt giữa 2 thiết kế: Khảo sát lặp lại (repeated measure) và nghiên cứu dài hạn (longitudinal study):

Repeated measure không bắt buộc có biến số thời gian. Đa số trường hợp, việc lặp lại thí nghiệm đồng nghĩa với việc khảo sát tại nhiều thời điểm khác nhau và theo trình tự thời gian; thí dụ trong trường hợp này, 6 liều thuốc khác nhau sẽ được đưa vào cơ thể mỗi con thỏ theo thứ tự tăng dần, bắt đầu từ D1 và kết thúc ở D6; khi đó yếu tố Thời gian đã hòa nhập làm một với yếu tố Can thiệp và Điều kiện thí nghiệm, và bị ẩn đi.

Trong thí dụ thứ 2, thậm chí người ta còn muốn triệt tiêu vai trò của thời điểm/thời gian, bằng cách ngẫu nhiên hóa thứ tự của thí nghiệm; thí dụ cá thể A được đo bằng 3 phương pháp theo trình tự M1,M2,M3, nhưng cá thể B thì đổi sang trình tự khác: M2,M1,M3. Một số trường hợp khác, thậm chí không có yếu tố thời gian vì các thí nghiệm được thực hiện đồng thời, thí dụ thí nghiệm In-vitro trong đó một đoạn mạch máu được cắt ra thành 3 phần và mỗi phần cho tiếp xúc với 1 hoạt chất co mạch khác nhau.

Trong khi đó, Thời gian là một biến số quan trọng, tuyến tính và biểu kiến trong longitudinal study. Thời gian này có thể rất dài (đúng như tên gọi : longitudinal), qua nhiều năm, tháng; nhưng cũng có thể rất ngắn, thí dụ bạn khảo sát sự biến thiên của 1 đoạn tín hiệu Y với tần số lấy mẫu 10 Hz, kéo dài 30 giây. Thời gian nên được khảo sát như 1 biến liên tục trong longitudinal study.

  1. Dạng chính tắc của ANOVA thường xem xét 2 hiệu ứng: between và within effect. Thí dụ thí nghiệm gốc trên thỏ này, between effect do hiệu ứng của đối vận serotonin, còn within effect do liều thuốc phenylbiguanide,

Trong giới hạn của bài này, chúng ta chỉ bàn về Repeated measure trong một trường hợp đơn giản nhất và đặc biệt, đó là cả 3 hiệu ứng Thời gian, Can thiệp, điều kiện khảo sát đã hòa nhập làm một trong biến số Dose step, tức là chỉ còn duy nhất within effect.

Tuy nhiên nguyên tắc của ANOVA là không đổi. Ta vẫn định nghĩa được F từ Mean square (MS) của mô hình, so với MS của Residual

Phương sai của kết quả Y có thể được giải thích qua một mô hình đơn giản như sau :

\[Y_{ij} \sim \mu + S_{i} + F_{j} + \varepsilon _{ij}\]

Mô hình này ước lượng giá trị Y cho cá thể i tại điều kiện thí nghiệm j từ : trung bình của Y trong quần thể là Mu, hiệu ứng ngẫu nhiên Si (random effect, S = subject, phần biến thiên ngẫu nhiên ở từng cá thể) và hiệu ứng chính do yếu tố F (Fixed effect, hay within subject variance, F ở đây là điều kiện thí nghiệm).

Phần sai số tồn lưu là epsilon, mô hình không thể giải thích được. Như vậy, việc đưa thêm random effect Si vào mô hình cho phép giảm thiểu sai số và phân lập được yếu tố ngẫu nhiên cá thể.

Mô hình này còn có tên là random intercept, vì Si chỉ đại diện cho trạng thái cơ bản của mỗi cá thể trước khi chịu can thiệp bởi thí nghiệm F, và ta giả định là mỗi cá thể sẽ đáp ứng như nhau trong thí nghiệm F.

Một mô hình khác phức tạp hơn, random slope còn xét cả sự tương tác ngẫu nhiên giữa Si và Fj. Tuy nhiên, trong trường hợp này ta chọn mô hình Random Intercept vì cỡ mẫu thấp và mỗi cá thể (i) chỉ được khảo sát 1 lần duy nhất tại mỗi điều kiện Fj.

Đồ thị của 5 mô hình tuyến tính có thể được hình dung như sau:

df%>%ggplot(aes(x=logDose,y=BPchange,fill=Animal))+
  geom_point(alpha=0.7,aes(color=step),show.legend = F,size=2)+
  geom_smooth(aes(col=Animal),
              size=1,
              method="lm",se=F,
              show.legend = F)+
  theme_bw()+
  scale_color_viridis(discrete=T,option="C",direction=-1)+
  scale_fill_viridis(discrete=T,option="C",direction=-1)

Như đã nói trong bài trước, các sinh viên Y khoa thường được dạy xử lý vấn đề theo hướng so sánh trung bình giữa các phân nhóm. Cách tư duy này có thể đúng cho hầu hết trường hợp, như thí dụ này quả thực ta đang muốn so sánh BPchange trung bình giữa 6 phân nhóm (bậc) D1:D6;

df%>%ggplot()+
  geom_boxplot(aes(x=step,y=BPchange,fill=step),
               alpha=0.7,show.legend = F)+
  geom_hline(yintercept = 0,linetype=2,col="red4")+
  theme_bw()+
  scale_color_viridis(discrete=T,option="C",direction=-1)+
  scale_fill_viridis(discrete=T,option="C",direction=-1)

Nhưng chúng tôi khuyến khích cách đặt mục tiêu : khảo sát hiệu ứng của yếu tố liều thuốc lên kết quả Y. Cách suy nghĩ này tổng quát, linh hoạt hơn và tự nhiên dẫn dắt bạn đến việc dùng mô hình.

ANOVA không được giảng dạy một cách hệ thống trong chương trình thống kê, nhất là ANOVA cho thí nghiệm lặp lại. Do đó, khi thực hiện nghiên cứu lâm sàng, các bác sĩ không biết đến phương pháp này. Thói quen thường gặp của họ là chia nhỏ bài toán ra thành nhiều cặp so sánh rồi dùng paired sample t-test hay Wilcoxon sign rank test để giải quyết. Việc này hoàn toàn sai.

Bài toán ANOVA cho phép đo lặp lại có thể được giải quyết bằng 2 cách: Thủ công/cổ điển hoặc Mô hình Mixed model:

Trước hết, ta xem xét các giả định

Mọi phân tích ANOVA đều yêu cầu những giả định như sau :

  1. Phân phối chuẩn :

Yj tại mỗi điều kiện thí nghiệm (phân nhóm) phải phân phối chuẩn chung quanh giá trị trung bình Muj.

df%>%
  split(.$step)%>%
  map(~fBasics::shapiroTest(.$BPchange))
## $D1
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9868
##   P VALUE:
##     0.9672 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin
## 
## 
## $D2
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.8261
##   P VALUE:
##     0.13 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin
## 
## 
## $D3
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9031
##   P VALUE:
##     0.4272 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin
## 
## 
## $D4
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.927
##   P VALUE:
##     0.576 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin
## 
## 
## $D5
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9415
##   P VALUE:
##     0.6768 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin
## 
## 
## $D6
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9149
##   P VALUE:
##     0.4974 
## 
## Description:
##  Sat Oct 28 21:31:21 2017 by user: Admin

Giả định này trên thực tế, hầu như không bao giờ được thỏa mãn, và những kiểm định thống kê để kiểm tra nó đều có nhược điểm nên cách xử trí tốt nhất là nhận xét trực quan và quyết định riêng của bạn (tính đồng dạng, outliers, …).

library(ggjoy)
## Warning: package 'ggjoy' was built under R version 3.4.2
## Loading required package: ggridges
## Warning: package 'ggridges' was built under R version 3.4.2
## The ggjoy package has been deprecated. Please switch over to the
## ggridges package, which provides the same functionality. Porting
## guidelines can be found here:
## https://github.com/clauswilke/ggjoy/blob/master/README.md
df%>%ggplot(aes(y=step,x=BPchange,
                fill=step,
                col=step
))+
  geom_joy(alpha=0.7,scale=1,show.legend = F)+
  geom_rug(alpha=0.6,show.legend = F)+
  geom_vline(xintercept = 0,linetype=2,col="red4")+
  coord_flip()+theme_bw()+
  scale_color_viridis(discrete=T,option="C",direction=-1)+
  scale_fill_viridis(discrete=T,option="C",direction=-1)
## Picking joint bandwidth of 2.16

Đồ thị cho thấy: Có sự vi phạm rõ ràng giả định phân phối chuẩn từ D3:D6, Shapiro - Wilk test đã không thể phát hiện. Mặt khác, cỡ mẫu quá thấp (n=5) cho mỗi phân nhóm - lẽ ra với cỡ mẫu này thậm chí ta phải dùng Non parametric test chứ không thể nghĩ đến một mô hình ANOVA. Vì lý do minh họa, ta sẽ thử đi tiếp:

  1. Tính độc lập của quan sát : điều này cho phép giả định về sự độc lập và ngẫu nhiên của sai số eij. Những cá thể trong từng phân nhóm phải độc lập với nhau. Thí nghiệm lặp lại là một trường hợp đặc biệt mà giả định này bị vi phạm một cách có hệ thống và chủ ý, vì mỗi cá thể tham gia vào tất cả bậc j của điều kiện thí nghiệm (phân nhóm), ta cũng đã hiệu chỉnh điều này bằng cách phân lập random effect Si và between subject effect Fj nên không phải bận tâm về giả định này.

  2. Phương sai đồng nhất : Mỗi phân nhóm (điều kiện thí nghiệm, bậc của F) phải có phương sai như nhau. Với thí nghiệm Repeated measure, ta có giả định quan trọng hơn là « Sphericity », hay tính đồng nhất về phương sai của các giá trị khác biệt ở mỗi cá thể (trong 1 covariance matrix). Thí dụ, nếu ta có K điều kiện thí nghiệm (lần lặp lại), thì sẽ có k(k-1)/2 cặp khác biệt, giả định sphericity là chúng đều có phương sai như nhau.

Giả định Sphericity có thể kiểm tra bằng test Mauchly, hoặc quan sát covariance matrix. Tuy nhiên bước này có thể bỏ qua nếu ta hiệu chỉnh df của within subject factor (F) bằng hệ số epsilon theo phương pháp Greenhouse-Geisser (GG) hoặc Huynh-Feildt (HF). Epsilon = 1 khi không có vi phạm giả định

Tuy nhiên, giả định sphericity là không cần thiết (không thể bị vi phạm), nếu K<3 (chỉ lặp lại thí nghiệm 2 lần). Sphericity cũng không cần thiết nếu trong mô hình không có tương tác giữa F và S (random intercept model, hay mỗi cá thể chỉ đo 1 lần tại mỗi điều kiện K) vì variance của khác biệt sẽ =0 như nhau cho mọi cá thể. Như vậy, trong trường hợp hiện tại, việc hiệu chỉnh df và kiểm tra sphericity là không cần thiết.

Phương pháp cổ điển: hàm aov

summary(aov(BPchange~step+Error(Animal), df))
## 
## Error: Animal
##           Df Sum Sq Mean Sq F value Pr(>F)
## Residuals  4  268.8   67.19               
## 
## Error: Within
##           Df Sum Sq Mean Sq F value   Pr(>F)    
## step       5   3752   750.4   60.13 2.32e-11 ***
## Residuals 20    250    12.5                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Hàm aov cho phép thực hiện F test đơn giản cho mô hình BPchange~step+Error(Animal)

Giải pháp Mixed model

Những lợi ích khi tiếp cận ANOVA cho thí nghiệm lặp lại bằng mô hình hỗn hợp (Mixed model):

Khả năng tùy biến cao hơn nhiều: bạn có thể mở rộng cấu trúc mô hình thoải mái, linh hoạt để thích nghi với độ phức tạp của thiết kế thí nghiệm. Thí dụ để khảo sát đồng thời nhiều Phương pháp trị liệu, tương tác với yếu tố Thời gian, những biến số khác, phân cụm bệnh nhân… Thậm chí, kết quả của bạn cũng không bắt buộc phải là biến định lượng (bạn có thể dựng mixed logistic model luôn).

Mô hình cho phép bạn xử lý yếu tố Thời gian như một biến số định lượng liên tục thực sự. ANOVA cổ điển chỉ nhìn thời điểm như các thứ bậc của biến định tính. Mixed model có thể mô hình hóa kết quả theo thời gian một cách liên tục chứ không chỉ ước lượng trung bình tại mỗi thời điểm. Điều này rất hữu ích nếu thí nghiệm được lặp lại định kỳ tại nhiều, rất nhiều thời điểm. Điều này đồng nghĩa với việc Thời gian có thể làm covariate (hiệp biến số) trong tương tác với một yếu tố khác.

Phương pháp mô hình cho phép dung nạp dữ liệu trống (missing value) và cỡ mẫu bất cân xứng giữa các phân nhóm. Trong quy trình ANOVA cổ điển, nếu dữ liệu thiếu sót tại bất cứ thời điểm nào thì toàn bộ dữ liệu còn lại của cá thể đó sẽ bị loại bỏ. Khi dùng mixed model, chỉ thời điểm có missing value bị loại bỏ, các bậc giá trị khác của Fj hay Tj vẫn được phân tích. Mixed model xử lý dữ liệu dưới dạng “long format” chứ không phải đa biến (wide format) nên số lượng không đồng đều giữa các phân nhóm không còn là vấn đề nữa.

Đơn giản hóa việc thực hiện suy diễn thống kê, thí dụ F test (vì lúc này Mean of squares chỉ còn hai bậc: Model và Residual). Từ mô hình cũng dễ dàng kiểm định ý nghĩa thống kê của từng bộ phận của phương sai, bao gồm cả random effects và covariance matrix. Cuối cùng, tư duy bằng ngôn ngữ mô hình là điều kiện quan trọng để bạn bước qua trường phái Bayes.

Ta có thể thực hiện Mixed model bằng 2 packages trong R: nlme và lme4

nlme package

# nlme method

library(nlme)
mod1=lme(BPchange~step, random=~1|Animal, df,method='REML')
summary(mod1)
## Linear mixed-effects model fit by REML
##  Data: df 
##        AIC      BIC    logLik
##   161.0754 170.4998 -72.53768
## 
## Random effects:
##  Formula: ~1 | Animal
##         (Intercept) Residual
## StdDev:    3.019727 3.532439
## 
## Fixed effects: BPchange ~ step 
##             Value Std.Error DF   t-value p-value
## (Intercept)  1.00  2.078311 20  0.481160  0.6356
## stepD2       1.35  2.234110 20  0.604267  0.5525
## stepD3       4.60  2.234110 20  2.058985  0.0528
## stepD4      16.40  2.234110 20  7.340729  0.0000
## stepD5      26.80  2.234110 20 11.995825  0.0000
## stepD6      26.20  2.234110 20 11.727262  0.0000
##  Correlation: 
##        (Intr) stepD2 stepD3 stepD4 stepD5
## stepD2 -0.537                            
## stepD3 -0.537  0.500                     
## stepD4 -0.537  0.500  0.500              
## stepD5 -0.537  0.500  0.500  0.500       
## stepD6 -0.537  0.500  0.500  0.500  0.500
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -1.86101507 -0.40461779  0.01142447  0.55587032  1.50371349 
## 
## Number of Observations: 30
## Number of Groups: 5
anova(mod1)
##             numDF denDF  F-value p-value
## (Intercept)     1    20 82.07769  <.0001
## step            5    20 60.13340  <.0001
intervals(mod1)
## Approximate 95% confidence intervals
## 
##  Fixed effects:
##                   lower  est.     upper
## (Intercept) -3.33528009  1.00  5.335280
## stepD2      -3.31027291  1.35  6.010273
## stepD3      -0.06027291  4.60  9.260273
## stepD4      11.73972709 16.40 21.060273
## stepD5      22.13972709 26.80 31.460273
## stepD6      21.53972709 26.20 30.860273
## attr(,"label")
## [1] "Fixed effects:"
## 
##  Random Effects:
##   Level: Animal 
##                    lower     est.    upper
## sd((Intercept)) 1.285609 3.019727 7.092946
## 
##  Within-group standard error:
##    lower     est.    upper 
## 2.591129 3.532439 4.815710
VarCorr(mod1)
## Animal = pdLogChol(1) 
##             Variance  StdDev  
## (Intercept)  9.118751 3.019727
## Residual    12.478125 3.532439

lme4 package

#lme4 method

library(lme4)

mod3=lmer(BPchange~step + (1|Animal), df,REML=TRUE)

library(lmerTest)

mod3=update(mod3)
summary(mod3)
## Linear mixed model fit by REML t-tests use Satterthwaite approximations
##   to degrees of freedom [lmerMod]
## Formula: BPchange ~ step + (1 | Animal)
##    Data: df
## 
## REML criterion at convergence: 145.1
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -1.86102 -0.40462  0.01142  0.55587  1.50371 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  Animal   (Intercept)  9.119   3.020   
##  Residual             12.478   3.532   
## Number of obs: 30, groups:  Animal, 5
## 
## Fixed effects:
##             Estimate Std. Error     df t value Pr(>|t|)    
## (Intercept)    1.000      2.078 12.689   0.481   0.6386    
## stepD2         1.350      2.234 20.000   0.604   0.5525    
## stepD3         4.600      2.234 20.000   2.059   0.0528 .  
## stepD4        16.400      2.234 20.000   7.341 4.29e-07 ***
## stepD5        26.800      2.234 20.000  11.996 1.37e-10 ***
## stepD6        26.200      2.234 20.000  11.727 2.04e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##        (Intr) stepD2 stepD3 stepD4 stepD5
## stepD2 -0.537                            
## stepD3 -0.537  0.500                     
## stepD4 -0.537  0.500  0.500              
## stepD5 -0.537  0.500  0.500  0.500       
## stepD6 -0.537  0.500  0.500  0.500  0.500
# Kenward-Roger denominator df method

anova(mod3,ddf="Kenward-Roger")
## Analysis of Variance Table of type III  with  Kenward-Roger 
## approximation for degrees of freedom
##      Sum Sq Mean Sq NumDF DenDF F.value    Pr(>F)    
## step 3751.8  750.35     5    20  60.133 2.315e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Ta có thể thấy cả 3 kết quả là tương đương. Theo đó, F test cho thấy Liều thuốc có hiệu ứng ý nghĩa làm tăng BPChange. (F(5,20)=60.133, p_value < 0.0001).

Giải pháp lai và hoàn chỉnh: package afex

afex là một package mới được tạo ra trong năm 2017, và đây là một công cụ mạnh nhất mà Nhi từng biết để thực hiện ANOVA. afex sử dụng cú pháp đơn giản và hỗ trợ tất cả thiết kế ANOVA với between, within và random effects, cho phép hiệu chỉnh vi phạm giả định sphericity bằng GG hay HF, cho phép làm posthoc testvới hiệu chỉnh ngưỡng ý nghĩa (thí dụ Bonferroni) và contrast analysis để cho ra kết quả phong phú không thua kém những software thương mại khác. Với repeated mesure ANOVA, afex dùng giải pháp lai, tức là phần lõi của nó dựa vào mixed model (package lme4), nhưng vẫn xuất ra kết quả theo truyền thống.

library(afex)

aovt=aov_ez(id="Animal",
            dv="BPchange",
            data=df,
            within=c("step"),
            anova_table = list(correction="GG",
                               es="pes",
                               p_adjust_method="bonferroni"))


summary(aovt)
## 
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
## 
##                 SS num Df Error SS den Df      F    Pr(>F)    
## (Intercept) 5514.9      1   268.76      4 82.078 0.0008227 ***
## step        3751.8      5   249.56     20 60.133 2.315e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
nice(aovt)
## Anova Table (Type 3 tests, bonferroni-adjusted)
## 
## Response: BPchange
##   Effect    df   MSE         F pes p.value
## 1   step 5, 20 12.48 60.13 *** .94  <.0001
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
## 
## Sphericity correction method: GG

Khi kết hợp afex và package lsmean, ta có một giải pháp rất mạnh để làm post-hoc test và contrast analysis:

library(lsmeans)

lsm=lsmeans(aovt,specs = "step")

lsm
##  step lsmean       SE    df  lower.CL  upper.CL
##  D1     1.00 2.078311 12.69 -3.501122  5.501122
##  D2     2.35 2.078311 12.69 -2.151122  6.851122
##  D3     5.60 2.078311 12.69  1.098878 10.101122
##  D4    17.40 2.078311 12.69 12.898878 21.901122
##  D5    27.80 2.078311 12.69 23.298878 32.301122
##  D6    27.20 2.078311 12.69 22.698878 31.701122
## 
## Confidence level used: 0.95
lsmdf=lsm%>%summary()%>%as_tibble()


lsmdf%>%ggplot(aes(x=step, 
                   y=lsmean,
                   fill=step,
                   group=1))+
  geom_errorbar(aes(ymin=lsmean-SE, 
                    ymax=lsmean+SE), 
                width=0.2,size=1) +
  geom_line(size=1,col="red4")+
  geom_point(size=5,shape=21,col="black")+
  scale_y_continuous("BPchange")+
  theme_bw()

Thí dụ, một phân tích tương phản với 2 giả thuyết H0 : Trung bình BPchange của 3 liều D123 tương đương với của D456, và: Trung bình BPChange là như nhau giữa 3 nhóm liều: D12, D34 và D56:

Ta chỉ cần đưa trọng số contrast vào matrix và dùng hàm contrast trên object lsm:

contrast(lsm, 
         list("D123/D456"=c(-1,-1,-1,1,1,1)/3,
              "D12/D34/D56"=c(-2,-2,-1,-1,1,1)/3),
         adjust="bonferroni")
##  contrast     estimate       SE    df t.ratio p.value
##  D123/D456   21.150000 1.289864 20.00  16.397  <.0001
##  D12/D34/D56  8.433333 2.563147 10.04   3.290  0.0162
## 
## P value adjustment: bonferroni method for 2 tests

Kết quả bác bỏ cả 2 giả thuyết H0 nêu trên, như vậy có tương phản ý nghĩa giữa nhóm liều thấp và liều cao, giữa 3 nhóm liều Thấp, Trung bình và Cao.

Ta cũng có thể làm test Contrast hàm đa thức bậc cao:

contrast(lsm,method="poly")
##  contrast  estimate        SE df t.ratio p.value
##  linear      219.15 13.217176 20  16.581  <.0001
##  quadratic    18.85 14.478691 20   1.302  0.2077
##  cubic       -94.35 21.194634 20  -4.452  0.0002
##  quartic     -16.25  8.359276 20  -1.944  0.0661
##  degree 5     16.95 25.077829 20   0.676  0.5068
pcdf=contrast(lsm,method="poly")%>%coef()%>%as_tibble()

colnames(pcdf)=c("Step","linear","Quadratic","Cubic","Quartic","Deg.5")

pcdf%>%gather(linear:Deg.5,key="Poly",value="Coef")%>%
  ggplot(aes(x=Step,y=Poly,fill=Coef))+
  geom_tile(col="black")+scale_fill_gradient2(low="blue",high="red",mid="white")+
  theme_bw()

pcdf%>%gather(linear:Deg.5,key="Poly",value="Coef")%>%
  ggplot(aes(x=Step,y=Coef,col=Poly,group=1))+
  geom_path()+facet_wrap(~Poly,ncol=2)+
  theme_bw()

Kết quả Polynomial contrast analysis cho thấy khuynh hướng thay đổi của BPchange có thể được xấp xỉ bằng 1 hàm bậc 3 (cubic) nghịch biến, hoặc bậc 1 (tuyến tính).Điều này có nghĩa gì: Huyết áp tăng dần theo liều thuốc (hàm linear); tuy nhiên ở những liều thấp nhất, huyết áp chỉ tăng nhẹ, tại một liều trung gian nào đó, sẽ tăng đột biến, sau đó sẽ duy trì pha bình nguyên ở những liều cao hơn (hàm cubic)

Một loại contrast analysis khác có thể thử, đó là reversed pairwise (bắt cặp tuần tự nghịch chiều), tức là so sánh giữa liều sau và liều thấp hơn::

contrast(lsm,"revpairwise",adjust="bonferroni")
##  contrast estimate       SE df t.ratio p.value
##  D2 - D1      1.35 2.234111 20   0.604  1.0000
##  D3 - D1      4.60 2.234111 20   2.059  0.7914
##  D3 - D2      3.25 2.234111 20   1.455  1.0000
##  D4 - D1     16.40 2.234111 20   7.341  <.0001
##  D4 - D2     15.05 2.234111 20   6.736  <.0001
##  D4 - D3     11.80 2.234111 20   5.282  0.0005
##  D5 - D1     26.80 2.234111 20  11.996  <.0001
##  D5 - D2     25.45 2.234111 20  11.392  <.0001
##  D5 - D3     22.20 2.234111 20   9.937  <.0001
##  D5 - D4     10.40 2.234111 20   4.655  0.0023
##  D6 - D1     26.20 2.234111 20  11.727  <.0001
##  D6 - D2     24.85 2.234111 20  11.123  <.0001
##  D6 - D3     21.60 2.234111 20   9.668  <.0001
##  D6 - D4      9.80 2.234111 20   4.387  0.0043
##  D6 - D5     -0.60 2.234111 20  -0.269  1.0000
## 
## P value adjustment: bonferroni method for 15 tests
cdf=contrast(lsm,"revpairwise",adjust="bonferroni")%>%
  coef()%>%
  as_tibble()

cdf%>%gather(c.1:c.15,key="Pairs",value="Coef")%>%
  ggplot(aes(x=step,y=Pairs,fill=as.factor(Coef)))+
  geom_tile(col="black")+scale_fill_manual(values=c("blue","white","red"))+
  theme_bw()

Kết quả cho thấy D4 chính là liều kích hoạt sự tăng huyết áp đột biến, so với 3 liều trước đó, sau đó huyết áp tăng dần từ D4-D5 nhưng bắt đầu giảm từ D5 đến D6.

3 Giải pháp Bayes cho thí nghiệm lặp lại

Trong mô hình STAN, chúng ta phân tích bài toán hiện thời theo thứ bậc như sau:

  • Giá trị của Y (BPchange) được mô tả bằng phân phối Gaussian (normal) với tham số : vị trí trung tâm Mu và scale Sigma, đây là hàm likelihood của STAN model

  • Giá trị Mu[ij] được ước lượng bằng một mô hình :

\[\mu _{ij} = \beta _{j}.X_{j} + \gamma_{i}\]

4 Mô hình Bayes trong STAN

Trong đó, Fixed effect – hiệu ứng thí nghiệm được ước tính như tích của 6 tham số beta(j) với j =1,2,3,4,5,6 và design matrix Xj với 6 predictors (D1,D2,D3,D4,D5,D6); Random effect – biến thiên ngẫu nhiên cá thể được ước lượng như 5 tham số gamma(i) tương ứng với 5 con thỏ trong thí nghiệm

  • Mô hình tường minh có nội dung:

\[\mu = \beta _{1}D1 + \beta _{2}D2 + \beta _{3}D3 + \beta _{4}D4 + \beta _{5}D5 + \beta _{6}D6 + \gamma_{i}\]

  • Mặt khác, ta giả định rằng gamma có phân phối chuẩn với 1 tham số scale là Sigma_B

  • Mục tiêu của chúng ta là tìm phân phối hậu định của : 6 tham số beta, tham số sigma, 5 tham số gamma và sigma_B

  • Về Prior (tiền định), ta có thể giả định như sau: beta có phân normal (0,100); gamma có phân phối normal(0,sigma_B), sigma_B và sigma có phân phối half Cauchy

  • Dữ liệu đầu vào của mô hình STAN gồm có:

  1. Cỡ mẫu n= độ dài matrix long data (n=30)

  2. Số dummy variables trong design matrix, hay bậc k của yếu tố can thiệp trong thí nghiệm, k=6 vì có 6 liều thuốc (mô hình này không chứa Intercept, một phần Intercept đã nằm trong tham số gammas).

  3. nB = số lượng cá thể, ở đây nB=5

  4. Vector biến kết quả Y, chính là BPchange

  5. Design matrix X chứa các dummy variable tạo ra từ 6 bậc yếu tố can thiệp là dose step

  6. Vector số nguyên B, là cột mã số định danh cho mỗi cá thể trong long data, lưu ý : vector này có độ dài = n chứ không phải nB

Ta viết cấu trúc mô hình này vào STAN code :

# STAN

rstanString="
data{
int n; //co mau
int nX; //predictors
int nB; // so block
vector [n] y;  // outcome
matrix [n,nX] X; // design matrix
int B[n]; //block vector
}

parameters{
vector [nX] beta; // fixed effect
real sigma; 
vector [nB] gamma; // random effect
real sigma_B;
}

transformed parameters {
vector[n] mu;  // fixed effect
mu = X*beta;
for (i in 1:n) {
mu[i]= mu[i] + gamma[B[i]];  //mean predicted
}

} 
model{
// Priors
beta ~ normal(0,100);
gamma ~ normal(0,sigma_B);
sigma_B ~ cauchy(0,25);
sigma ~ cauchy(0,25);

//likelihood
y ~ normal( mu , sigma );
}
"

Sau khi code mô hình, ta sẽ tạo ra dữ liệu đầu vào cho phân tích Bayes, dưới dạng 1 list, trong đó ta khai báo các thành phần mà block data đã mô tả.

Xmat=model.matrix(~step, data=df)

datalist=with(df, list(y=BPchange, X=Xmat, nX=ncol(Xmat),
                       B=as.integer(Animal),
                       n=nrow(df), 
                       nB=length(levels(Animal))))

Ta đưa data vào mô hình và kích hoạt quy trình lấy mẫu MCMC với các tùy chỉnh Ta sẽ chạy 4 chuỗi song song: Trước hết sampler sẽ chạy 1000 lượt khởi động, khi ổn định thì kết quả MCMC bắt đầu được ghi lại 2000 lượt cho mỗi chuỗi sau đó rút gọn 1/2 để cuối cùng chỉ còn 1000 giá trị x 4 chuỗi = 4000 giá trị cho mỗi tham số, và chạy song song trên tối đa 4 cores của máy tính.

library(rstan)

rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())

set.seed(123)

fit <- stan(data=datalist,
                           model_code=rstanString,
                           chains=4,
                           iter=3000,
                           warmup=1000,
                           thin=2,
            control=list(adapt_delta=0.99))
## 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 file3743d06efc.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"

Quy trình converged cực nhanh vì dữ liệu có n thấp. Bây giờ ta có 1 object tương đối lớn, chứa 4 chuỗi MCMCx1000 lượt cho 6 tham số beta, sigma, sigma_B, 5 tham số gamma, và 30 giá trị ước lượng cho Mu.

5 Phân phối hậu định cho Fixed và Random effects

Vì ta đang có trong tay phân phối hậu định của tất cả tham số (thành phần) của mô hình, ta có thể lần lượt khai thác chúng, bắt đầu bằng Fixed effect (hiệu ứng chính)

Fixed effect

Phân phối hậu định của 6 tham số beta (chính là trung bình của BPchange cho 6 bậc của dosestep, hay fixed effect của dose step sau khi hiệu chỉnh cho random effect) có thể được khảo sát như sau :

postdf=as.data.frame(fit)%>%as_tibble()

betadf=postdf[,c(1:6)]%>%mutate(.,Iteration=as.numeric(rep(c(1:1000),4)),
                                  Chain=as.factor(rep(c(1:4),each=1000)))

colnames(betadf)=c("D1","D2","D3","D4","D5","D6","Iteration","Chain")

head(betadf)%>%.[,c(1:6)]%>%knitr::kable()
D1 D2 D3 D4 D5 D6
-3.600280 -0.3920784 2.712608 17.35059 25.99524 26.35438
-2.990803 -1.2436725 2.734570 13.21955 25.03401 24.06605
-5.783254 1.1506698 2.456025 17.42084 27.39022 27.65323
-5.046266 5.7077594 4.030842 19.15349 30.08561 27.24923
-2.275975 -2.9764782 3.723750 12.18430 23.75162 23.44466
-1.372470 4.2986273 6.874671 18.53192 25.72957 31.57401
p1=betadf%>%gather(D1:D6,key="Step",value="Fixed_effect")%>%
  ggplot(aes(x=Fixed_effect,fill=Chain,col=Chain))+
  geom_density(alpha=0.3,show.legend = F)+
  facet_wrap(~Step,ncol=1,scales = "free_y")+
  theme_bw(6)
  
p2=betadf%>%gather(D1:D6,key="Step",value="Fixed_effect")%>%
  ggplot(aes(y=Fixed_effect,x=Iteration,col=Chain))+
  geom_path(alpha=0.5,show.legend = F)+
  facet_wrap(~Step,ncol=1,scales = "free")+
  theme_bw(6)

gridExtra::grid.arrange(p2,p1,ncol=2)

# Plot

betadf$pseudoGroup=factor(rep(c(1:20),e=nrow(betadf)/20))

betadf%>%gather(D1:D6,key="Step",value="Fixed_effect")%>%
ggplot()+
  geom_joy(aes(y=Step,
              x=Fixed_effect,
              col=pseudoGroup,
              fill=Step),
           alpha=0.03,show.legend = F,scale=1)+
  geom_vline(xintercept=median(df$BPchange),col="red3",linetype=2)+
  scale_x_continuous(breaks=c(-20,-10,0,10,20,30,40,50))+
  coord_flip()+
  scale_colour_brewer(palette = "PuRd")+
  scale_fill_brewer(palette="Reds")+
  theme_bw()

Tiếp theo, ta sẽ khảo sát Random effects, hay 5 tham số gammas cho 5 con thỏ:

Gammas cho biết sai biệt ngẫu nhiên, cơ bản và cá thể của từng con thỏ bên ngoài hiệu ứng chính của 6 liều thuốc

Random effects

# Random effects (gammas)

gammadf=postdf[,c(8:12)]%>%mutate(.,Iteration=as.numeric(rep(c(1:1000),4)),
                                Chain=as.factor(rep(c(1:4),each=1000)))

colnames(gammadf)=c("R1","R2","R3","R4","R5","Iteration","Chain")

gammadf$pseudoGroup=factor(rep(c(1:20),e=nrow(gammadf)/20))

gammadf%>%gather(R1:R5,key="Blocks",value="Random_effect")%>%
  ggplot()+
  geom_joy(aes(y=Blocks,
               x=Random_effect,
               col=pseudoGroup,
               fill=Blocks),
           alpha=0.03,show.legend = F,scale=1)+
  geom_vline(xintercept=0,col="blue3",linetype=2)+
  coord_flip()+
  scale_colour_brewer(palette = "Blues")+
  scale_fill_brewer(palette="Blues")+
  theme_bw()

Ta thậm chí có thể khảo sát phân phối hậu nghiệm cho Mu (Yhat) của 5 con thỏ tại từng bậc của dosestep:

# Y_hat

yhatdf=postdf[,c(14:43)]%>%mutate(.,Iteration=as.numeric(rep(c(1:1000),4)),
                                  Chain=as.factor(rep(c(1:4),each=1000)))

yhatdf$pseudoGroup=factor(rep(c(1:20),e=nrow(yhatdf)/20))

yhatdf%>%gather(`mu[1]`:`mu[30]`,key="Observation",value="Y_hat")%>%
  mutate(.,Step=rep(c("D1","D2","D3","D4","D5","D6"),each=20000))%>%
  ggplot()+
  geom_joy(aes(y=Observation,
               x=Y_hat,
               col=pseudoGroup,
               fill=Step),
           alpha=0.02,show.legend = F,scale=1)+
  geom_vline(xintercept=median(df$BPchange),col="blue3",linetype=2)+
  scale_fill_viridis(discrete=T,option="C",direction = -1)+
  scale_color_brewer(palette="RdPu")+
  theme_bw()+
  facet_wrap(~Step,ncol=2,scales="free_y")

6 Bảng ANOVA từ mô hình BAYES

Nội dung của bước này là tương tự như cách ta làm để dựng bảng ANOVA cổ điển, chỉ khác đó là MSM và MSR và các sum of squares không được xác định từ mô hình tuyến tính dùng algorithm Least square hay REML, mà dùng chính mô hình Bayes với 6 tham số Beta (fiex effect) và 5 tham số gamma (random effect). Để đơn giản, ta lấy Trung vị của phân phối hậu định của mỗi tham số để tạo ra 1 mô hình đại diện.

Đầu tiên, ta tính Y_hat từ mô hình đại diện như sau:

coefs=as.matrix(fit)%>%.[,c(1:6)]%>%apply(.,2, median)

gams=as.matrix(fit)%>%.[,c(8:12)]%>%apply(.,2, median)
gam=c(rep(gams[[1]],6),
      rep(gams[[2]],6),
      rep(gams[[3]],6),
      rep(gams[[4]],6),
      rep(gams[[5]],6)
      )

predDF=data.frame(x=df$step,truth=df$BPchange)

Yhat=(coefs %*% t(model.matrix(~predDF$x))+gam)%>%as.vector()

Sau đó, từ mô hình đại diện Bayes này, ta tính các SS, MSM, MSR, F values cũng như các effect-sizes.

Ghi chú: Bậc tự do của các thành phần được tính như sau:

\[df_{total}= (N-1)\]

\[df_{fix}= (k-1)\] \[df_{random}= (s-1)\]

\[df_{residual} = (N-k-s+1)\] Ở đây, Dose step là k=6 do có 6 bậc giá trị của Dosestep, s=5 cho 5 con thỏ, N=30.

Ta bắt đầu tính thủ công msm, msr, F, p_value cho F test và effectsizes là eta2 và omega2:

df1=5
df2=30-5-6+1
n=30
k=6

ssm=sum( (Yhat-mean(predDF$truth))^2 )
ssr=sum( (predDF$truth-Yhat)^2 )
sst=sum( (predDF$truth-mean(predDF$truth))^2 )
msm=ssm/df1
msr=ssr/df2
f=msm/msr

eta2=ssm/sst
r=sqrt(ssm/sst)
R2=r^2
omega2=(ssm-df1*msr)/(sst+msr) # or ((k-1)*(F-1))/((k-1)*(F-1)*(n*k))
#2-sides F test
p2sides=2*(1-(pf(f,df1,df2,F)))
#1-side F test
p1side=(1-(pf(f,df1,df2,F)))

rbind(ssm,ssr,sst,msm,msr,f,df1,df2,p1side,p2sides,eta2,omega2,r,R2)%>%round(.,3)%>%as.matrix()%>%.[,1]
##      ssm      ssr      sst      msm      msr        f      df1      df2 
## 3906.821  265.877 4270.085  781.364   13.294   58.776    5.000   20.000 
##   p1side  p2sides     eta2   omega2        r       R2 
##    0.000    0.000    0.915    0.897    0.957    0.915

Như vậy, đây là một giải pháp “lai” giữa hồi quy Bayes và null hypothesis testing. Thậm chí ta có cả p values.

Kết quả trên đây có thể xem như tương đương với kết quả của quy trình ANOVA theo trường phái Frequentist. Giá trị F gần như nhau (60 so với 58.8), p_value <0.001

7 Post-hoc test theo BAYES

Quy trình này thực sự thay thế post-hoc test theo phái frequentist bằng phương pháp Bayes. Nội dung của post hoc test theo Bayes rất đơn giản: Ta sẽ tạo ra 15 chuỗi MCMC chứa phân phối hậu định của “khác biệt trung bình” giữa 2 phân nhóm được bắt cặp tuần tự (Ta có 6 phân nhóm, nên có 15 cặp so sánh ). Suy diễn Bayes sẽ được thực hiện trực tiếp trên mỗi chuỗi MCMC, không cần dùng p_value hay hiệu chỉnh.

posthocdf=data.frame(
  D1D2=rep(NA,4000),
  D1D3=rep(NA,4000),
  D1D4=rep(NA,4000),
  D1D5=rep(NA,4000),
  D1D6=rep(NA,4000),
  D2D3=rep(NA,4000),
  D2D4=rep(NA,4000),
  D2D5=rep(NA,4000),
  D2D6=rep(NA,4000),
  D3D4=rep(NA,4000),
  D3D5=rep(NA,4000),
  D3D6=rep(NA,4000),
  D4D5=rep(NA,4000),
  D4D6=rep(NA,4000),
  D5D6=rep(NA,4000)
)

for (i in 1:4000) {
  posthocdf$D1D2[i]=betadf$D2[i]-betadf$D1[i]
  posthocdf$D1D3[i]=betadf$D3[i]-betadf$D1[i]
  posthocdf$D1D4[i]=betadf$D4[i]-betadf$D1[i]
  posthocdf$D1D5[i]=betadf$D5[i]-betadf$D1[i]
  posthocdf$D1D6[i]=betadf$D6[i]-betadf$D1[i]
  posthocdf$D2D3[i]=betadf$D3[i]-betadf$D2[i]
  posthocdf$D2D4[i]=betadf$D4[i]-betadf$D2[i]
  posthocdf$D2D5[i]=betadf$D5[i]-betadf$D2[i]
  posthocdf$D2D6[i]=betadf$D6[i]-betadf$D2[i]
  posthocdf$D3D4[i]=betadf$D4[i]-betadf$D3[i]
  posthocdf$D3D5[i]=betadf$D5[i]-betadf$D3[i]
  posthocdf$D3D6[i]=betadf$D6[i]-betadf$D3[i]
  posthocdf$D4D5[i]=betadf$D5[i]-betadf$D4[i]
  posthocdf$D4D6[i]=betadf$D6[i]-betadf$D4[i]
  posthocdf$D5D6[i]=betadf$D6[i]-betadf$D5[i]
}

posthocdf$pseudoGroup=factor(rep(c(1:20),e=nrow(posthocdf)/20))

Suy diễn Bayes được thực hiện bằng cả 2 phương pháp : ROPE (Kruschke) và Bayes Factor.

Trước hết, ta hãy xem qua phân tích ROPE phân phối hậu định của 6 khác biệt trung bình :

Nhắc lại, ROPE là một khoảng vô nghĩa thực dụng, thí dụ ta giả địng rằng nếu khác biệt trung bình nằm trong khoảng +/- 5 xem như vô nghĩa.

# Bayes Inference

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)
}

posthocdf=posthocdf%>%gather(D1D2:D5D6,key="Pairs",value="Difference")

posthocdf%>%
  split(.$Pairs)%>%
  map(~summaryKruschke(MCMC = .$Difference,
                       compVal=0.0,
                       rope=c(-5,5),
                       credMass=0.975)
  )%>%as.data.frame()->kruschke

colnames(kruschke)=unique(posthocdf$Pairs)
kruschke%>%t()%>%round(.,2)
##       Mean Median  Mode HDIlevel    LL    UL CompVal PcntGtCompVal ROPElow
## D1D2  0.36   0.34  0.15     0.98 -9.81 10.31       0         53.48      -5
## D1D3  3.59   3.59  3.34     0.98 -6.61 14.04       0         80.22      -5
## D1D4 15.42  15.35 14.54     0.98  5.76 24.89       0         99.85      -5
## D1D5 25.76  25.79 26.09     0.98 15.88 36.53       0        100.00      -5
## D1D6 25.15  25.14 25.01     0.98 15.31 35.93       0        100.00      -5
## D2D3  3.23   3.25  3.40     0.98 -2.85  8.86       0         90.60      -5
## D2D4 15.06  14.99 14.96     0.98  9.19 20.56       0        100.00      -5
## D2D5 25.40  25.43 25.64     0.98 20.05 31.21       0        100.00      -5
## D2D6 24.79  24.74 24.19     0.98 19.06 30.38       0        100.00      -5
## D3D4 11.82  11.78 11.67     0.98  6.37 17.62       0        100.00      -5
## D3D5 22.17  22.19 22.63     0.98 16.41 28.06       0        100.00      -5
## D3D6 21.56  21.53 21.19     0.98 15.47 26.94       0        100.00      -5
## D4D5 10.35  10.34 10.08     0.98  5.01 16.23       0         99.97      -5
## D4D6  9.74   9.74  9.42     0.98  3.88 15.28       0        100.00      -5
## D5D6 -0.61  -0.65 -1.35     0.98 -6.12  5.37       0         39.40      -5
##      ROPEhigh PcntLtROPE PcntInROPE PcntGtROPE
## D1D2        5      11.12      74.67      14.20
## D1D3        5       2.85      59.30      37.85
## D1D4        5       0.00       0.88      99.12
## D1D5        5       0.00       0.00     100.00
## D1D6        5       0.00       0.00     100.00
## D2D3        5       0.08      77.47      22.45
## D2D4        5       0.00       0.03      99.97
## D2D5        5       0.00       0.00     100.00
## D2D6        5       0.00       0.00     100.00
## D3D4        5       0.00       0.55      99.45
## D3D5        5       0.00       0.00     100.00
## D3D6        5       0.00       0.00     100.00
## D4D5        5       0.00       1.58      98.42
## D4D6        5       0.00       2.97      97.03
## D5D6        5       3.25      95.22       1.52
posthocdf%>%ggplot()+
  geom_joy(aes(y=reorder(Pairs,Difference),
               x=Difference,
               fill=reorder(Pairs,Difference),
               col=pseudoGroup),
           alpha=0.05,
           show.legend = F,
           scale=1)+
  geom_vline(xintercept=c(-5,0.0,5),col="red4",linetype=2)+
  ylab("Pairs")+
  theme_bw()+ggtitle("Bayesian Pairwise comparison")+
  scale_color_brewer(palette="PuRd")+
  scale_fill_viridis(discrete=T,option="C",direction = -1)

Kết quả: Ngoại trừ cặp D1D3, D2D3,D1D2 và D5D6, tương ứng với việc chuyển từ liều thấp nhất sang liều kế cận cho đến D4, và từ liều cao nhất D5 sang 1 liều cao hơn là D6 không cho thấy khác biệt rõ nét; thì 2 cặp D3D4 và D4D5 đều cho thấy một sự tương phản khá rõ, vì mật độ phân phối hậu định của khác biệt trung bình nằm trong ROPE là 0.55% (cặp D3D4),và 1.58% (D4D5), trong khi 98.4% và 99.5% mật độ khác biệt trung bình nằm ngoài ROPE và > 5.

Kết quả này hoàn toàn tương đồng với kết quả của Contrast analysis. Chúng tôi không muốn áp đặt ý kiến riêng nào cả về kết quả này. Các bạn có toàn quyền Tin hoặc không Tin về khác biệt có ý nghĩa cho việc chuyển liều từ D3 sang D4 và D5.

Có một cách khác nếu bạn muốn phân định Trắng/Đen: đó là dùng Bayes Factor, với ngưỡng so sánh là zero (không có khác biệt), tỉ trọng chứng cứ ủng hộ cho giả thuyết H1 (khác biệt < 0):

Do có quá nhiều cặp so sánh (15), ta có thể khảo sát trực quan độ lớn của BF cho 15 ngưỡng so sánh từ -10 đến +20 trong 1 heatmap như sau:

# Bayes Factors

library(brms)

thres=c(-10:20)

threshold=rep(NA,15*length(thres))
BayesFactor=rep(NA,15*length(thres))
Pair=rep(NA,15*length(thres))

pairlev=unique(posthocdf$Pairs)

n=0

for(i in (1:15)){
  for (j in (1:length(thres))){
    tempdf=subset(posthocdf,Pairs==pairlev[i])
    Pair[n+j]=pairlev[i]
    thr=thres[j]
    threshold[n+j]=thr
    hyp=paste("Difference>",thr,sep="")
    bf=hypothesis(tempdf,hyp,alpha=0.05)
    BayesFactor[n+j]=bf$hypothesis$Evid.Ratio
  }
  n=n+length(thres)
}

tempbfdf=cbind(Pair,threshold,BayesFactor)%>%as_tibble()
tempbfdf$threshold=as.numeric(tempbfdf$threshold)
tempbfdf$BayesFactor=as.numeric(tempbfdf$BayesFactor)

for(i in (1:nrow(tempbfdf))){
  tempbfdf$BayesFactor[i]<-ifelse(tempbfdf$BayesFactor[i]>9999,9999,
                                  tempbfdf$BayesFactor[i])
}
tempbfdf%>%ggplot(aes(x=threshold,y=reorder(Pair,BayesFactor),fill=BayesFactor,col=BayesFactor))+
  geom_tile(alpha=0.7)+
  theme_bw()+
  geom_vline(xintercept=c(0,5),col="blue3")+
  scale_color_viridis(discrete=F,option="D",direction=-1)+
  scale_fill_viridis(discrete=F,option="D",direction=-1)

Kết quả cho thấy BF rất cao cho ngưỡng so sánh =0 và các cặp D4D5 (BF khoảng 3000) và D3D4 (BF > 9000)

8 Diễn đạt văn bản khoa học

Phương pháp thống kê

Hiệu ứng của liều thuốc phenylbiguanide trên sự thay đổi của huyết áp thỏ được khảo sát bằng một mô hình Bayes cho phép đo lặp lại với liều thuốc (k=6) là hiệu ứng chính, sai biệt cơ bản ở từng cá thể (i=5) là hiệu ứng ngẫu nhiên. Mô hình này cho ra phân phối hậu nghiệm của trung bình khác biệt của huyết áp khi chuyển từ liều thấp sang liều cao hơn. Suy diễn thống kê dựa vào HDI (khoảng mật độ cao nhất), ROPE (theo Kruschke) và tỉ trọng chứng cứ (Bayes factor).

Kết quả

Mô hình Bayes cho thấy liều thuốc gây ra hiệu ứng ý nghĩa làm tăng huyết áp (F(5,20)=58.78, p_value<0.0001 ; omegasquared = 0.897). Phân phối hậu định của giá trị huyết áp trung bình và trung bình khác biệt giữa các liều thuốc được trình bày trong hình … Phân tích ROPE (bảng…) và BF factor (Hình…) cho thấy khuynh hướng như sau : Huyết áp không thay đổi ý nghĩa trong phạm vi liều D1 đến D3, nhưng tăng đột ngột khi chuyển từ D3 sang D4, sau đó tăng nhẹ khi liều tăng từ D4 sang D5 và giảm dần khi chuyển từ D5 qua D6.

9 Bàn luận và Tổng kết

Tuy thí dụ minh họa trong bài chưa hoàn hảo đối với thí nghiệm ANOVA repeated measured vì không phân biệt rõ between, within effect và yếu tố thời gian; nhưng đây là một trường hợp thú vị, cho thấy ưu thế của phương pháp Bayes so với ANOVA cổ điển; vì thực ra, trên dữ liệu này ta không thể làm ANOVA được (cỡ mẫu quá thấp: n=5 cho mỗi phân nhóm, vi phạm giả định phân phối chuẩn); nhưng khi dùng Bayes không cầnbận tâm đến chuyện này. Việc quy định likelihood và prior trong mô hình Bayes cũng là giả định, nhưng nó không bắt buộc, không cố định. Nếu muốn, ta dễ dàng dùng một likelihood theo phân phối khác cho Y (thí dụ Gamma hay logNormal…), ta cũng có thể thay đổi prior tùy thích. Lý do ta chọn phân phối normal chỉ đơn giản vì đây là giả định hợp lý nhất cho biến liên tục khi ta không có một thông tin nào khác.

Đây là lần đầu tiên các bạn làm Mixed model trong STAN, vấn đề là: Khi dùng phương pháp Bayes, các bạn không cần quan tâm đến cơ chế riêng của algorithm cho từng loại mô hình (việc đó đã có MCMC sampler lo), mà chỉ cần quan tâm đến việc giải phẫu cấu trúc mô hình và mô tả cấu trúc này trong STAN. Chỉ với 2 parameters, ta có thể thêm Random effect vào model. Trong thời gian tới, nhóm BAV sẽ giới thiệu cho các bạn những mô hình còn phức tạp hơn,như thay đổi family distribution cho likelihood thành Poisson chẳng hạn.

Các thông điệp quan trọng được chuyển tải trong bài:

  1. ANOVA cho thí nghiệm lặp lại là một trường hợp tổng quát cho paired sample t test

  2. Cần phân biệt giữa Repeated measure và Longitudinal study, để áp dụng phù hợp.

  3. Nên tiếp cận Repeated measure ANOVA bằng Mixed model thay cho giải pháp đa biến và tính thủ công. Mixed model linh hoạt hơn nhiều.

  4. Phương pháp suy diễn Bayes linh hoạt và có nhiều ưu thế hơn so với Null hypothesis testing và P_value

Nhóm BAV xin chân thành cảm ơn sự ủng hộ của các bạn. Chúng tôi sẽ gặp lại các bạn trong một bài khác.

LS0tDQp0aXRsZTogIkJBWUVTLUFOT1ZBIGNobyB0aMOtIG5naGnhu4dtIGzhurdwIGzhuqFpIg0Kc3VidGl0bGU6ICJT4butIGThu6VuZyBuZ8O0biBuZ+G7ryBTVEFOIg0KYXV0aG9yOiAiTMOqIE5n4buNYyBLaOG6oyBOaGkiDQpkYXRlOiAiMjggVGjDoW5nIDEwIDIwMTciDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJzdGFuKQ0KbGlicmFyeShicm1zKQ0KYGBgDQoNCiFbXShyZXBBTk9WQUJheWVzMS5wbmcpDQoNCipHaGkgY2jDujoqIA0KDQpCcy4gTMOqIE5n4buNYyBLaOG6oyBOaGkgbMOgIHRow6BuaCB2acOqbiB0cm9uZyBDb3JldGVhbSBj4bunYSBuaMOzbSBCQVYgKEJheWVzaWFuIGFuYWx5c2lzIGZvciBWaWV0bmFtKS4gVHJvbmcgYsOgaSB2aeG6v3QgY8OzIHPhu60gZOG7pW5nIDEgc+G7kSDDvSB0xrDhu59uZywgUiBmdW5jdGlvbiB2w6AgY29kZSBj4bunYSBhbmggTmjhuq10IE5hbSBsw6AgdGVhbSBsZWFkZXIuIEPhuqNtIMahbiBi4bqhbiDEkGluaCBUaeG6v24gVMOgaSB0cm9uZyB2aeG7h2Mga2nhu4NtIHRyYSBTVEFOIGNvZGUuDQoNCiMgR2nhu5tpIHRoaeG7h3UNCg0KVGjDom4gY2jDoG8gY8OhYyBi4bqhbiwgxJHDonkgbMOgIGLDoGkgdGjhu7FjIGjDoG5oIHRo4bupIDYgdHJvbmcgZOG7sSDDoW4gQmF5ZXMgZm9yIFZpZXRuYW0uIE3hu6VjIHRpw6p1IGPhu6dhIGNow7puZyB0w7RpIGzDoCBwaOG7lSBj4bqtcCB24buBIHBoxrDGoW5nIHBow6FwIHRo4buRbmcga8OqIHRoZW8gdHLGsOG7nW5nIHBow6FpIEJheWVzIG5o4bqxbSB0aGF5IHRo4bq/IGhvw6BuIHRvw6BuIG5o4buvbmcgY8O0bmcgY+G7pSB0cnV54buBbiB0aOG7kW5nLiBO4buRaSB0aeG6v3AgYsOgaSB0csaw4bubYyB24bubaSBjaOG7pyDEkeG7gSBBTk9WQSDEkcahbiBiaeG6v24sIGLDoGkgbsOgeSBz4bq9IGdp4bubaSB0aGnhu4d1IHbhu5tpIGPDoWMgYuG6oW4gdGhp4bq/dCBr4bq/IFRow60gbmdoaeG7h20gbOG6t3AgbOG6oWkgKFJlcGVhdGVkIG1lYXN1cmUpIHbDoCBjw6FjaCBwaMOibiB0w61jaCBk4buvIGxp4buHdSBsb+G6oWkgbsOgeS4gTmhpIGdp4bqjIMSR4buLbmggbMOgIGPDoWMgYuG6oW4gxJHDoyBxdWVuIHRodeG7mWMgduG7m2kgY8ahIGNo4bq/IGPhu6dhIHN1eSBkaeG7hW4gQmF5ZXMsIGPhuqV1IHRyw7pjIG5nw7RuIG5n4buvIFNUQU4gdsOgIHF1eSB0csOsbmgga2hhaSB0aMOhYyBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCwgbsOqbiBt4buZdCBz4buRIHBo4bqnbiBz4bq9IMSRxrDhu6NjIGdp4bqjbiBsxrDhu6NjLg0KDQpUcm9uZyBiw6BpIG7DoHkgY2jDum5nIHRhIHPhur0gdGhheSB0aOG6vyBwaMOibiB0w61jaCBwaMawxqFuZyBzYWkgY2hvIHRow60gbmdoaeG7h20gbOG6t3AgbOG6oWkgKFJlcGVhdGVkIG1lYXN1cmUgQU5PVkEpIGLhurFuZyBtw7QgaMOsbmggaOG7l24gaOG7o3AgdsOgIGjhu5NpIHF1eSBCYXllcy4gTmjGsCB0aMaw4budbmcgbOG7hywgYsOgaSBnaeG6o25nIHPhur0gxJFpIHRoZW8gMyBixrDhu5tjIG5oxrAgc2F1OiANCg0KVHLGsOG7m2MgaOG6v3QsIGNow7puZyB0w7RpIHPhur0gw7RuIGzhuqFpIGzDvSB0aHV54bq/dCB24buBIEFOT1ZBIHJlcGVhdGVkIG1lYXN1cmUsIMSRxrBhIHJhIDEgYsOgaSB0b8OhbiB2w6AgbWluaCBo4buNYSBxdXkgdHLDrG5oIEFOT1ZBIGPhu5UgxJFp4buDbiB0aGVvIHBow6FpIGZyZXF1ZW50aXN0LiANCg0KU2F1IMSRw7MgY2jDum5nIHTDtGkgc+G6vSBjaHV54buDbiBiw6BpIHRvw6FuIG7DoHkgdGjDoG5oIG3DtCBow6xuaCBCYXllcywgdsOgIGjGsOG7m25nIGThuqtuIGPDoWMgYuG6oW4gdmnhur90IFNUQU4gY29kZSBjaG8gbcO0IGjDrG5oLg0KDQpDdeG7kWkgY8O5bmcsIGNow7puZyB0YSBz4bq9IGtoYWkgdGjDoWMgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY2hvIGPDoWMgdGhhbSBz4buRIGPhuqduIHF1YW4gdMOibSwgdsOgIHN1eSBkaeG7hW4gQmF5ZXMuDQoNCiMgw5RuIHThuq1wIHbhu4EgQU5PVkEgdGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaQ0KDQoqQsOgaSB0b8OhbiBtaW5oIGjhu41hKg0KDQpE4buvIGxp4buHdSBtaW5oIGjhu41hIHRyb25nIGLDoGkgbsOgeSBsw6AgbeG7mXQgdGjDrSBuZ2hp4buHbSBzaW5oIGzDvSB0csOqbiDEkeG7mW5nIHbhuq10IHbDoG8gbsSDbSAxOTk0IGPhu6dhIEdTLiBKLiBMdWRicm9vay4gVHJvbmcgdGjDrSBuZ2hp4buHbSBuZ3V5w6puIHRo4buneSwgbeG7pWMgdGnDqnUgbmjhurFtIGto4bqjbyBzw6F0IGhp4buHdSDhu6luZyB0xrDGoW5nIHTDoWMgY+G7p2EgaG/huqF0IGNo4bqldCBwaGVueWxiaWd1YW5pZGUgKGvDrWNoIHRow61jaCBjbyBt4bqhY2gpIHbDoCAxIGNo4bqldCDEkeG7kWkgduG6rW4gc2Vyb3RvbmluIChjw7MgdMOhYyBk4bulbmcgZ2nhuqNtIGNvIG3huqFjaCkgZ8OieSB0aGF5IMSR4buVaSBodXnhur90IMOhcCDhu58gNSBjb24gdGjhu48uIMSQ4buDIMSRxqFuIGdp4bqjbiB24bqlbiDEkeG7gSwgY2jDum5nIHTDtGkgbG/huqFpIGLhu48geeG6v3UgdOG7kSBjYW4gdGhp4buHcCB24bubaSDEkeG7kWkgduG6rW4gc2Vyb3RvbmluLCB2w6AgY2jhu4kgZ2nhu68gbOG6oWkgbmjDs20gY2jhu6luZyB2w6AgaGnhu4d1IOG7qW5nIGPhu6dhIHBoZW55bGJpZ3VhbmlkZS4NCg0KQ8OidSBo4buPaSBuZ2hpw6puIGPhu6l1IGdp4bqjIMSR4buLbmggdHJvbmcgYsOgaSB0b8OhbiBoaeG7h24gdOG6oWksIGRvIMSRw7Mgc+G6vSBsw6A6IGto4bqjbyBzw6F0IGhp4buHdSDhu6luZyBj4bunYSBsaeG7gXUgdGh14buRYyBwaGVueWxiaWd1YW5pZGUgbMOqbiBodXnhur90IMOhcCBj4bunYSB0aOG7jy4gDQoNClF1eSB0csOsbmggdGjDrSBuZ2hp4buHbSBsw6AgbmjGsCBzYXU6IDUgY29uIHRo4buPIHPhur0gxJHGsOG7o2MgdGnDqm0gdMSpbmggbeG6oWNoIGzhuqduIGzGsOG7o3QgNiBsaeG7gXUgcGhlbnlsYmlndWFuaWRlIHThu6sgRDEgKHRo4bqlcCBuaOG6pXQpIMSR4bq/biBENiAoY2FvIG5o4bqldCkgduG7m2kga2hv4bqjbmcgY8OhY2ggMTAgcGjDunQuIEh1eeG6v3Qgw6FwIHRo4buPIMSRxrDhu6NjIMSRbyBjw7luZyBsw7pjIChuaMawIHbhuq15IG3hu5dpIGNvbiB0aOG7jyDEkcaw4bujYyDEkW8gaHV54bq/dCDDoXAgNiBs4bqnbikuDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmRmPXJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWIuY29tL3ZpbmNlbnRhcmVsYnVuZG9jay9SZGF0YXNldHMvbWFzdGVyL2Nzdi9NQVNTL1JhYmJpdC5jc3YiKQ0KDQojIFdpdGhpbiBvbmx5DQoNCmRmJHN0ZXA9cmVwKGMoIkQxIiwiRDIiLCJEMyIsIkQ0IiwiRDUiLCJENiIpLDUpDQoNCmRmPXN1YnNldChkZixUcmVhdG1lbnQ9PSJDb250cm9sIiklPiUuWyxjKDI6Myw2LDcpXSU+JWFzX3RpYmJsZSgpJT4lbXV0YXRlKGxvZ0Rvc2U9bG9nKC4kRG9zZSkpDQoNCmhlYWQoZGYpJT4la25pdHI6OmthYmxlKCkNCg0KYGBgDQoNCkLhuqNuZyB0aOG7kW5nIGvDqiBtw7QgdOG6ozogDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KIyBEZXNjcmlwdGl2ZSBhbmFseXNpcw0KDQpkPXBzeWNoOjpkZXNjcmliZUJ5KGRmJEJQY2hhbmdlLGRmJHN0ZXApDQoNCnQxPXJiaW5kKGQkRDFbLGMoMjoxMyldLCANCiAgICAgICAgIGQkRDJbLGMoMjoxMyldLCANCiAgICAgICAgIGQkRDNbLGMoMjoxMyldLCANCiAgICAgICAgIGQkRDRbLGMoMjoxMyldLA0KICAgICAgICAgZCRENVssYygyOjEzKV0sDQogICAgICAgICBkJEQ2WyxjKDI6MTMpXSklPiVhcy5kYXRhLmZyYW1lKCklPiVyb3VuZCguLDIpDQoNCnJvdy5uYW1lcyh0MSk9YygiRDEiLCJEMiIsIkQzIiwiRDQiLCJENSIsIkQ2IikNCg0Ka25pdHI6OmthYmxlKHQxKQ0KYGBgDQoNCkLDonkgZ2nhu50gY2jDum5nIHRhIHPhur0gcGjDom4gdMOtY2ggYsOgaSB0b8OhbiBuw6B5Lg0KDQpL4bq/dCBxdeG6oyBsw6AgQlBjaGFuZ2UsIDEgYmnhur9uIMSR4buLbmggbMaw4bujbmcgbGnDqm4gdOG7pWMuIE3hu5dpIGNvbiB0aOG7jyDEkeG7gXUgdGhhbSBnaWEgdsOgbyBj4bqjIDYgbOG6p24gxJFvIHTGsMahbmcg4bupbmcgRDEgxJHhur9uIEQ2LiBLaOG6o28gc8OhdCB0cuG7sWMgcXVhbiBjaG8gdGjhuqV5IGtodXluaCBoxrDhu5tuZyBjaHVuZyBsw6AgQlBjaGFuZ2UgdMSDbmcgZOG6p24gdOG7qyBEMSDEkeG6v24gRDYgduG7m2kgZGFvIMSR4buZbmcgbmfhuqt1IG5oacOqbiB0w7l5IHRoZW8gY8OhIHRo4buDIHRo4buPOw0KDQpgYGB7cn0NCiMgRXhwbG9yYXRpb24NCg0KbGlicmFyeSh2aXJpZGlzKQ0KDQpkZiU+JWdncGxvdChhZXMoeD1zdGVwLHk9QlBjaGFuZ2UsZmlsbD1BbmltYWwsZ3JvdXA9MSkpKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPUFuaW1hbCksc2l6ZT0xKSsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMSxzaXplPTMsY29sPSJibGFjayIpKw0KICB0aGVtZV9idygpKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlPVQsb3B0aW9uPSJDIixkaXJlY3Rpb249LTEpKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VCxvcHRpb249IkMiLGRpcmVjdGlvbj0tMSkrDQogIGZhY2V0X3dyYXAofkFuaW1hbCxzY2FsZT0iZnJlZSIsbmNvbD0yKQ0KDQpgYGANCg0KVGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaSBsw6AgbeG7mXQgdGhp4bq/dCBr4bq/IG5naGnDqm4gY+G7qXUgdHJvbmcgxJHDsyBr4bq/dCBxdeG6oyDEkeG7i25oIGzGsOG7o25nIFlpaiBz4bq9IMSRxrDhu6NjIGto4bqjbyBzw6F0IG5oaeG7gXUgbOG6p24gdOG6oWkgbmhp4buBdSB0aOG7nWkgxJFp4buDbSAoVGopIGtow6FjIG5oYXUgaG/hurdjIGTGsOG7m2kgbmjhu69uZyDEkWnhu4F1IGtp4buHbi9waMawxqFuZyBwaMOhcCAoRmopIGtow6FjIG5oYXUgdMOqbiBjw7luZyDEkeG7kWkgdMaw4bujbmcgKFNpKS4NCg0KVGhp4bq/dCBr4bq/IG7DoHkgY8OzIHLhuqV0IG5oaeG7gXUg4bupbmcgZOG7pW5nIHRyb25nIG5naGnDqm4gY+G7qXUgeSBo4buNYywgdGjDrSBk4bulOg0KDQorIMSQw6FuaCBnacOhIGhp4buHdSBxdeG6oyDEkWnhu4F1IHRy4buLIGPhu6dhIHBoxrDGoW5nIHBow6FwIHBo4bqrdSB0aHXhuq10OiBt4buXaSBi4buHbmggbmjDom4gxJHGsOG7o2Mga2jhuqNvIHPDoXQgMSBjaOG7iSBz4buRIGzDom0gc8OgbmcgWSB0csaw4bubYyB2w6Agc2F1IGtoaSBwaOG6q3UgdGh14bqtdCAobMawdSDDvTogUmVwZWF0ZWQgbWVhc3VyZSBBTk9WQSBsw6AgZOG6oW5nIHThu5VuZyBxdcOhdCBj4bunYSBwYWlyZWQgc2FtcGxlcyBTdHVkZW50IHQtdGVzdCkuDQoNCisgS2jhuqNvIHPDoXQgdMOtbmggdMawxqFuZyBo4bujcCBj4bunYSAzIG3DoXkgxJFvIMSRxrDhu51uZyBodXnhur90OiBDw7luZyBt4buZdCBi4buHbmggbmjDom4gc+G6vSDEkcaw4bujYyDEkW8gxJHGsOG7nW5nIGh1eeG6v3QgYuG7n2kgMyBtw6F5IMSRbywgdGhlbyB0aOG7qSB04buxIG5n4bqrdSBuaGnDqm4uIE3hu5l0IHRoaeG6v3Qga+G6vyB0xrDGoW5nIHThu7E6IGto4bqjbyBzw6F0IHPhu7EgdMawxqFuZyBo4bujcCBjaOG6qW4gxJFvw6FuIGPhu6dhIDMgYsOhYyBzxKkgY2jhuqluIMSRb8OhbiBow6xuaCDhuqNuaCBraGkgY2hvIGjhu40gxJHhu41jIGvhur90IHF14bqjIGPhu6dhIGPDuW5nIDEgcGhpbSBDVC1zY2FuLg0KDQorIE5naGnDqm4gY+G7qXUgdGhlbyBkw7VpIGvDqW8gZMOgaSAobG9uZ2l0dWRpbmFsIHN0dWR5KTogTeG7mXQgbmjDs20gYuG7h25oIG5ow6JuIMSRxrDhu6NjIHRoZW8gZMO1aSBkaeG7hW4gdGnhur9uIGPhu6dhIG1hcmtlciBsw6JtIHPDoG5nIFkgbeG7l2kgdGjDoW5nIG3hu5l0IGzhuqduIHRyb25nIHN14buRdCBxdcOhIHRyw6xuaCDEkWnhu4F1IHRy4buLIGLhurFuZyBsb+G6oWkgdGh14buRYyBYLCB0cm9uZyB0aOG7nWkgZ2lhbiAxIG7Eg20uIA0KDQouLi4NCg0KQ8OzIDIgY2hpIHRp4bq/dCBxdWFuIHRy4buNbmcgY+G6p24gbMawdSDDvTogDQoNCjEpIEPhuqduIHBow6JuIGJp4buHdCBnaeG7r2EgMiB0aGnhur90IGvhur86IEto4bqjbyBzw6F0IGzhurdwIGzhuqFpIChyZXBlYXRlZCBtZWFzdXJlKSB2w6AgbmdoacOqbiBj4bupdSBkw6BpIGjhuqFuIChsb25naXR1ZGluYWwgc3R1ZHkpOg0KDQpSZXBlYXRlZCBtZWFzdXJlIGtow7RuZyBi4bqvdCBideG7mWMgY8OzIGJp4bq/biBz4buRIHRo4budaSBnaWFuLiDEkGEgc+G7kSB0csaw4budbmcgaOG7o3AsIHZp4buHYyBs4bq3cCBs4bqhaSB0aMOtIG5naGnhu4dtIMSR4buTbmcgbmdoxKlhIHbhu5tpIHZp4buHYyBraOG6o28gc8OhdCB04bqhaSBuaGnhu4F1IHRo4budaSDEkWnhu4NtIGtow6FjIG5oYXUgdsOgIHRoZW8gdHLDrG5oIHThu7EgdGjhu51pIGdpYW47IHRow60gZOG7pSB0cm9uZyB0csaw4budbmcgaOG7o3AgbsOgeSwgNiBsaeG7gXUgdGh14buRYyBraMOhYyBuaGF1IHPhur0gxJHGsOG7o2MgxJHGsGEgdsOgbyBjxqEgdGjhu4MgbeG7l2kgY29uIHRo4buPIHRoZW8gdGjhu6kgdOG7sSB0xINuZyBk4bqnbiwgYuG6r3QgxJHhuqd1IHThu6sgRDEgdsOgIGvhur90IHRow7pjIOG7nyBENjsga2hpIMSRw7MgeeG6v3UgdOG7kSBUaOG7nWkgZ2lhbiDEkcOjIGjDsmEgbmjhuq1wIGzDoG0gbeG7mXQgduG7m2kgeeG6v3UgdOG7kSBDYW4gdGhp4buHcCB2w6AgxJBp4buBdSBraeG7h24gdGjDrSBuZ2hp4buHbSwgdsOgIGLhu4sg4bqpbiDEkWkuDQoNClRyb25nIHRow60gZOG7pSB0aOG7qSAyLCB0aOG6rW0gY2jDrSBuZ8aw4budaSB0YSBjw7JuIG114buRbiB0cmnhu4d0IHRpw6p1IHZhaSB0csOyIGPhu6dhIHRo4budaSDEkWnhu4NtL3Ro4budaSBnaWFuLCBi4bqxbmcgY8OhY2ggbmfhuqt1IG5oacOqbiBow7NhIHRo4bupIHThu7EgY+G7p2EgdGjDrSBuZ2hp4buHbTsgdGjDrSBk4bulIGPDoSB0aOG7gyBBIMSRxrDhu6NjIMSRbyBi4bqxbmcgMyBwaMawxqFuZyBwaMOhcCB0aGVvIHRyw6xuaCB04buxIE0xLE0yLE0zLCBuaMawbmcgY8OhIHRo4buDIEIgdGjDrCDEkeG7lWkgc2FuZyB0csOsbmggdOG7sSBraMOhYzogTTIsTTEsTTMuIE3hu5l0IHPhu5EgdHLGsOG7nW5nIGjhu6NwIGtow6FjLCB0aOG6rW0gY2jDrSBraMO0bmcgY8OzIHnhur91IHThu5EgdGjhu51pIGdpYW4gdsOsIGPDoWMgdGjDrSBuZ2hp4buHbSDEkcaw4bujYyB0aOG7sWMgaGnhu4duIMSR4buTbmcgdGjhu51pLCB0aMOtIGThu6UgdGjDrSBuZ2hp4buHbSBJbi12aXRybyB0cm9uZyDEkcOzIG3hu5l0IMSRb+G6oW4gbeG6oWNoIG3DoXUgxJHGsOG7o2MgY+G6r3QgcmEgdGjDoG5oIDMgcGjhuqduIHbDoCBt4buXaSBwaOG6p24gY2hvIHRp4bq/cCB4w7pjIHbhu5tpIDEgaG/huqF0IGNo4bqldCBjbyBt4bqhY2gga2jDoWMgbmhhdS4gDQoNClRyb25nIGtoaSDEkcOzLCBUaOG7nWkgZ2lhbiBsw6AgbeG7mXQgYmnhur9uIHPhu5EgcXVhbiB0cuG7jW5nLCB0dXnhur9uIHTDrW5oIHbDoCBiaeG7g3Uga2nhur9uIHRyb25nIGxvbmdpdHVkaW5hbCBzdHVkeS4gVGjhu51pIGdpYW4gbsOgeSBjw7MgdGjhu4MgcuG6pXQgZMOgaSAoxJHDum5nIG5oxrAgdMOqbiBn4buNaSA6IGxvbmdpdHVkaW5hbCksIHF1YSBuaGnhu4F1IG7Eg20sIHRow6FuZzsgbmjGsG5nIGPFqW5nIGPDsyB0aOG7gyBy4bqldCBuZ+G6r24sIHRow60gZOG7pSBi4bqhbiBraOG6o28gc8OhdCBz4buxIGJp4bq/biB0aGnDqm4gY+G7p2EgMSDEkW/huqFuIHTDrW4gaGnhu4d1IFkgduG7m2kgdOG6p24gc+G7kSBs4bqleSBt4bqrdSAxMCBIeiwga8OpbyBkw6BpIDMwIGdpw6J5LiBUaOG7nWkgZ2lhbiBuw6puIMSRxrDhu6NjIGto4bqjbyBzw6F0IG5oxrAgMSBiaeG6v24gbGnDqm4gdOG7pWMgdHJvbmcgbG9uZ2l0dWRpbmFsIHN0dWR5Lg0KDQoyKSBE4bqhbmcgY2jDrW5oIHThuq9jIGPhu6dhIEFOT1ZBIHRoxrDhu51uZyB4ZW0geMOpdCAyIGhp4buHdSDhu6luZzogYmV0d2VlbiB2w6Agd2l0aGluIGVmZmVjdC4gVGjDrSBk4bulIHRow60gbmdoaeG7h20gZ+G7kWMgdHLDqm4gdGjhu48gbsOgeSwgYmV0d2VlbiBlZmZlY3QgZG8gaGnhu4d1IOG7qW5nIGPhu6dhIMSR4buRaSB24bqtbiBzZXJvdG9uaW4sIGPDsm4gd2l0aGluIGVmZmVjdCBkbyBsaeG7gXUgdGh14buRYyBwaGVueWxiaWd1YW5pZGUsIA0KDQohW10ocmVwQU5PVkFCYXllczJiLnBuZykNCg0KVHJvbmcgZ2nhu5tpIGjhuqFuIGPhu6dhIGLDoGkgbsOgeSwgY2jDum5nIHRhIGNo4buJIGLDoG4gduG7gSBSZXBlYXRlZCBtZWFzdXJlIHRyb25nIG3hu5l0IHRyxrDhu51uZyBo4bujcCDEkcahbiBnaeG6o24gbmjhuqV0IHbDoCDEkeG6t2MgYmnhu4d0LCDEkcOzIGzDoCBj4bqjIDMgaGnhu4d1IOG7qW5nIFRo4budaSBnaWFuLCBDYW4gdGhp4buHcCwgxJFp4buBdSBraeG7h24ga2jhuqNvIHPDoXQgxJHDoyBow7JhIG5o4bqtcCBsw6BtIG3hu5l0IHRyb25nIGJp4bq/biBz4buRIERvc2Ugc3RlcCwgdOG7qWMgbMOgIGNo4buJIGPDsm4gZHV5IG5o4bqldCB3aXRoaW4gZWZmZWN0Lg0KDQpUdXkgbmhpw6puIG5ndXnDqm4gdOG6r2MgY+G7p2EgQU5PVkEgbMOgIGtow7RuZyDEkeG7lWkuIFRhIHbhuqtuIMSR4buLbmggbmdoxKlhIMSRxrDhu6NjIEYgdOG7qyBNZWFuIHNxdWFyZSAoTVMpIGPhu6dhIG3DtCBow6xuaCwgc28gduG7m2kgTVMgY+G7p2EgUmVzaWR1YWwNCg0KYGBge3J9DQoNCmBgYA0KDQohW10ocmVwQU5PVkFCYXllczJhLnBuZykNCg0KUGjGsMahbmcgc2FpIGPhu6dhIGvhur90IHF14bqjIFkgY8OzIHRo4buDIMSRxrDhu6NjIGdp4bqjaSB0aMOtY2ggcXVhIG3hu5l0IG3DtCBow6xuaCDEkcahbiBnaeG6o24gbmjGsCBzYXUgOg0KDQokJFlfe2lqfSBcc2ltIFxtdSArIFNfe2l9ICsgRl97an0gKyBcdmFyZXBzaWxvbiBfe2lqfSQkDQoNCk3DtCBow6xuaCBuw6B5IMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIFkgY2hvIGPDoSB0aOG7gyBpIHThuqFpIMSRaeG7gXUga2nhu4duIHRow60gbmdoaeG7h20gaiB04burIDogdHJ1bmcgYsOsbmggY+G7p2EgWSB0cm9uZyBxdeG6p24gdGjhu4MgbMOgIE11LCBoaeG7h3Ug4bupbmcgbmfhuqt1IG5oacOqbiBTaSAocmFuZG9tIGVmZmVjdCwgUyA9IHN1YmplY3QsIHBo4bqnbiBiaeG6v24gdGhpw6puIG5n4bqrdSBuaGnDqm4g4bufIHThu6tuZyBjw6EgdGjhu4MpIHbDoCBoaeG7h3Ug4bupbmcgY2jDrW5oIGRvIHnhur91IHThu5EgRiAoRml4ZWQgZWZmZWN0LCBoYXkgd2l0aGluIHN1YmplY3QgdmFyaWFuY2UsIEYg4bufIMSRw6J5IGzDoCDEkWnhu4F1IGtp4buHbiB0aMOtIG5naGnhu4dtKS4NCg0KUGjhuqduIHNhaSBz4buRIHThu5NuIGzGsHUgbMOgIGVwc2lsb24sIG3DtCBow6xuaCBraMO0bmcgdGjhu4MgZ2nhuqNpIHRow61jaCDEkcaw4bujYy4gTmjGsCB24bqteSwgdmnhu4djIMSRxrBhIHRow6ptIHJhbmRvbSBlZmZlY3QgU2kgdsOgbyBtw7QgaMOsbmggY2hvIHBow6lwIGdp4bqjbSB0aGnhu4N1IHNhaSBz4buRIHbDoCBwaMOibiBs4bqtcCDEkcaw4bujYyB54bq/dSB04buRIG5n4bqrdSBuaGnDqm4gY8OhIHRo4buDLg0KDQpNw7QgaMOsbmggbsOgeSBjw7JuIGPDsyB0w6puIGzDoCByYW5kb20gaW50ZXJjZXB0LCB2w6wgU2kgY2jhu4kgxJHhuqFpIGRp4buHbiBjaG8gdHLhuqFuZyB0aMOhaSBjxqEgYuG6o24gY+G7p2EgbeG7l2kgY8OhIHRo4buDIHRyxrDhu5tjIGtoaSBjaOG7i3UgY2FuIHRoaeG7h3AgYuG7n2kgdGjDrSBuZ2hp4buHbSBGLCB2w6AgdGEgZ2nhuqMgxJHhu4tuaCBsw6AgbeG7l2kgY8OhIHRo4buDIHPhur0gxJHDoXAg4bupbmcgbmjGsCBuaGF1IHRyb25nIHRow60gbmdoaeG7h20gRi4gDQoNCk3hu5l0IG3DtCBow6xuaCBraMOhYyBwaOG7qWMgdOG6oXAgaMahbiwgcmFuZG9tIHNsb3BlIGPDsm4geMOpdCBj4bqjIHPhu7EgdMawxqFuZyB0w6FjIG5n4bqrdSBuaGnDqm4gZ2nhu69hIFNpIHbDoCBGai4gVHV5IG5oacOqbiwgdHJvbmcgdHLGsOG7nW5nIGjhu6NwIG7DoHkgdGEgY2jhu41uIG3DtCBow6xuaCBSYW5kb20gSW50ZXJjZXB0IHbDrCBj4buhIG3huqt1IHRo4bqlcCB2w6AgbeG7l2kgY8OhIHRo4buDIChpKSBjaOG7iSDEkcaw4bujYyBraOG6o28gc8OhdCAxIGzhuqduIGR1eSBuaOG6pXQgdOG6oWkgbeG7l2kgxJFp4buBdSBraeG7h24gRmouDQoNCsSQ4buTIHRo4buLIGPhu6dhIDUgbcO0IGjDrG5oIHR1eeG6v24gdMOtbmggY8OzIHRo4buDIMSRxrDhu6NjIGjDrG5oIGR1bmcgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KZGYlPiVnZ3Bsb3QoYWVzKHg9bG9nRG9zZSx5PUJQY2hhbmdlLGZpbGw9QW5pbWFsKSkrDQogIGdlb21fcG9pbnQoYWxwaGE9MC43LGFlcyhjb2xvcj1zdGVwKSxzaG93LmxlZ2VuZCA9IEYsc2l6ZT0yKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbD1BbmltYWwpLA0KICAgICAgICAgICAgICBzaXplPTEsDQogICAgICAgICAgICAgIG1ldGhvZD0ibG0iLHNlPUYsDQogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRikrDQogIHRoZW1lX2J3KCkrDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXMoZGlzY3JldGU9VCxvcHRpb249IkMiLGRpcmVjdGlvbj0tMSkrDQogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1ULG9wdGlvbj0iQyIsZGlyZWN0aW9uPS0xKQ0KDQpgYGANCg0KTmjGsCDEkcOjIG7Ds2kgdHJvbmcgYsOgaSB0csaw4bubYywgY8OhYyBzaW5oIHZpw6puIFkga2hvYSB0aMaw4budbmcgxJHGsOG7o2MgZOG6oXkgeOG7rSBsw70gduG6pW4gxJHhu4EgdGhlbyBoxrDhu5tuZyBzbyBzw6FuaCB0cnVuZyBiw6xuaCBnaeG7r2EgY8OhYyBwaMOibiBuaMOzbS4gQ8OhY2ggdMawIGR1eSBuw6B5IGPDsyB0aOG7gyDEkcO6bmcgY2hvIGjhuqd1IGjhur90IHRyxrDhu51uZyBo4bujcCwgbmjGsCB0aMOtIGThu6UgbsOgeSBxdeG6oyB0aOG7sWMgdGEgxJFhbmcgbXXhu5FuIHNvIHPDoW5oIEJQY2hhbmdlIHRydW5nIGLDrG5oIGdp4buvYSA2IHBow6JuIG5ow7NtIChi4bqtYykgRDE6RDY7DQoNCmBgYHtyfQ0KZGYlPiVnZ3Bsb3QoKSsNCiAgZ2VvbV9ib3hwbG90KGFlcyh4PXN0ZXAseT1CUGNoYW5nZSxmaWxsPXN0ZXApLA0KICAgICAgICAgICAgICAgYWxwaGE9MC43LHNob3cubGVnZW5kID0gRikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsbGluZXR5cGU9Mixjb2w9InJlZDQiKSsNCiAgdGhlbWVfYncoKSsNCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhkaXNjcmV0ZT1ULG9wdGlvbj0iQyIsZGlyZWN0aW9uPS0xKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVQsb3B0aW9uPSJDIixkaXJlY3Rpb249LTEpDQoNCmBgYA0KDQpOaMawbmcgY2jDum5nIHTDtGkga2h1eeG6v24ga2jDrWNoIGPDoWNoIMSR4bq3dCBt4bulYyB0acOqdSA6IGto4bqjbyBzw6F0IGhp4buHdSDhu6luZyBj4bunYSB54bq/dSB04buRIGxp4buBdSB0aHXhu5FjIGzDqm4ga+G6v3QgcXXhuqMgWS4gQ8OhY2ggc3V5IG5naMSpIG7DoHkgdOG7lW5nIHF1w6F0LCBsaW5oIGhv4bqhdCBoxqFuIHbDoCB04buxIG5oacOqbiBk4bqrbiBk4bqvdCBi4bqhbiDEkeG6v24gdmnhu4djIGTDuW5nIG3DtCBow6xuaC4NCg0KQU5PVkEga2jDtG5nIMSRxrDhu6NjIGdp4bqjbmcgZOG6oXkgbeG7mXQgY8OhY2ggaOG7hyB0aOG7kW5nIHRyb25nIGNoxrDGoW5nIHRyw6xuaCB0aOG7kW5nIGvDqiwgbmjhuqV0IGzDoCBBTk9WQSBjaG8gdGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaS4gRG8gxJHDsywga2hpIHRo4buxYyBoaeG7h24gbmdoacOqbiBj4bupdSBsw6JtIHPDoG5nLCBjw6FjIGLDoWMgc8SpIGtow7RuZyBiaeG6v3QgxJHhur9uIHBoxrDGoW5nIHBow6FwIG7DoHkuIFRow7NpIHF1ZW4gdGjGsOG7nW5nIGfhurdwIGPhu6dhIGjhu40gbMOgIGNoaWEgbmjhu48gYsOgaSB0b8OhbiByYSB0aMOgbmggbmhp4buBdSBj4bq3cCBzbyBzw6FuaCBy4buTaSBkw7luZyBwYWlyZWQgc2FtcGxlIHQtdGVzdCBoYXkgV2lsY294b24gc2lnbiByYW5rIHRlc3QgxJHhu4MgZ2nhuqNpIHF1eeG6v3QuIFZp4buHYyBuw6B5IGhvw6BuIHRvw6BuIHNhaS4NCg0KQsOgaSB0b8OhbiBBTk9WQSBjaG8gcGjDqXAgxJFvIGzhurdwIGzhuqFpIGPDsyB0aOG7gyDEkcaw4bujYyBnaeG6o2kgcXV54bq/dCBi4bqxbmcgMiBjw6FjaDogVGjhu6cgY8O0bmcvY+G7lSDEkWnhu4NuIGhv4bq3YyBNw7QgaMOsbmggTWl4ZWQgbW9kZWw6DQoNCipUcsaw4bubYyBo4bq/dCwgdGEgeGVtIHjDqXQgY8OhYyBnaeG6oyDEkeG7i25oKg0KDQpN4buNaSBwaMOibiB0w61jaCBBTk9WQSDEkeG7gXUgecOqdSBj4bqndSBuaOG7r25nIGdp4bqjIMSR4buLbmggbmjGsCBzYXUgOg0KDQoxKSBQaMOibiBwaOG7kWkgY2h14bqpbiA6IA0KDQpZaiB04bqhaSBt4buXaSDEkWnhu4F1IGtp4buHbiB0aMOtIG5naGnhu4dtIChwaMOibiBuaMOzbSkgcGjhuqNpIHBow6JuIHBo4buRaSBjaHXhuqluIGNodW5nIHF1YW5oIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggTXVqLg0KDQpgYGB7cn0NCmRmJT4lDQogIHNwbGl0KC4kc3RlcCklPiUNCiAgbWFwKH5mQmFzaWNzOjpzaGFwaXJvVGVzdCguJEJQY2hhbmdlKSkNCmBgYA0KDQpHaeG6oyDEkeG7i25oIG7DoHkgdHLDqm4gdGjhu7FjIHThur8sIGjhuqd1IG5oxrAga2jDtG5nIGJhbyBnaeG7nSDEkcaw4bujYyB0aOG7j2EgbcOjbiwgdsOgIG5o4buvbmcga2nhu4NtIMSR4buLbmggdGjhu5FuZyBrw6ogxJHhu4Mga2nhu4NtIHRyYSBuw7MgxJHhu4F1IGPDsyBuaMaw4bujYyDEkWnhu4NtICBuw6puIGPDoWNoIHjhu60gdHLDrSB04buRdCBuaOG6pXQgbMOgIG5o4bqtbiB4w6l0IHRy4buxYyBxdWFuIHbDoCBxdXnhur90IMSR4buLbmggcmnDqm5nIGPhu6dhIGLhuqFuICh0w61uaCDEkeG7k25nIGThuqFuZywgb3V0bGllcnMsIOKApikuIA0KIA0KYGBge3J9DQpsaWJyYXJ5KGdnam95KQ0KDQpkZiU+JWdncGxvdChhZXMoeT1zdGVwLHg9QlBjaGFuZ2UsDQogICAgICAgICAgICAgICAgZmlsbD1zdGVwLA0KICAgICAgICAgICAgICAgIGNvbD1zdGVwDQopKSsNCiAgZ2VvbV9qb3koYWxwaGE9MC43LHNjYWxlPTEsc2hvdy5sZWdlbmQgPSBGKSsNCiAgZ2VvbV9ydWcoYWxwaGE9MC42LHNob3cubGVnZW5kID0gRikrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsbGluZXR5cGU9Mixjb2w9InJlZDQiKSsNCiAgY29vcmRfZmxpcCgpK3RoZW1lX2J3KCkrDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXMoZGlzY3JldGU9VCxvcHRpb249IkMiLGRpcmVjdGlvbj0tMSkrDQogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1ULG9wdGlvbj0iQyIsZGlyZWN0aW9uPS0xKQ0KYGBgDQoNCsSQ4buTIHRo4buLIGNobyB0aOG6pXk6IEPDsyBz4buxIHZpIHBo4bqhbSByw7UgcsOgbmcgZ2nhuqMgxJHhu4tuaCBwaMOibiBwaOG7kWkgY2h14bqpbiB04burIEQzOkQ2LCAgU2hhcGlybyAtIFdpbGsgdGVzdCDEkcOjIGtow7RuZyB0aOG7gyBwaMOhdCBoaeG7h24uIE3hurd0IGtow6FjLCBj4buhIG3huqt1IHF1w6EgdGjhuqVwIChuPTUpIGNobyBt4buXaSBwaMOibiBuaMOzbSAtIGzhur0gcmEgduG7m2kgY+G7oSBt4bqrdSBuw6B5IHRo4bqtbSBjaMOtIHRhIHBo4bqjaSBkw7luZyBOb24gcGFyYW1ldHJpYyB0ZXN0IGNo4bupIGtow7RuZyB0aOG7gyBuZ2jEqSDEkeG6v24gbeG7mXQgbcO0IGjDrG5oIEFOT1ZBLiBWw6wgbMO9IGRvIG1pbmggaOG7jWEsIHRhIHPhur0gdGjhu60gxJFpIHRp4bq/cDoNCg0KMikgVMOtbmggxJHhu5ljIGzhuq1wIGPhu6dhIHF1YW4gc8OhdCA6IMSRaeG7gXUgbsOgeSBjaG8gcGjDqXAgZ2nhuqMgxJHhu4tuaCB24buBIHPhu7EgxJHhu5ljIGzhuq1wIHbDoCBuZ+G6q3Ugbmhpw6puIGPhu6dhIHNhaSBz4buRIGVpai4gTmjhu69uZyBjw6EgdGjhu4MgdHJvbmcgdOG7q25nIHBow6JuIG5ow7NtIHBo4bqjaSDEkeG7mWMgbOG6rXAgduG7m2kgbmhhdS4gVGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaSBsw6AgbeG7mXQgdHLGsOG7nW5nIGjhu6NwIMSR4bq3YyBiaeG7h3QgbcOgIGdp4bqjIMSR4buLbmggbsOgeSBi4buLIHZpIHBo4bqhbSBt4buZdCBjw6FjaCBjw7MgaOG7hyB0aOG7kW5nIHbDoCBjaOG7pyDDvSwgdsOsIG3hu5dpIGPDoSB0aOG7gyB0aGFtIGdpYSB2w6BvIHThuqV0IGPhuqMgYuG6rWMgaiBj4bunYSDEkWnhu4F1IGtp4buHbiB0aMOtIG5naGnhu4dtIChwaMOibiBuaMOzbSksIHRhIGPFqW5nIMSRw6MgaGnhu4d1IGNo4buJbmggxJFp4buBdSBuw6B5IGLhurFuZyBjw6FjaCBwaMOibiBs4bqtcCByYW5kb20gZWZmZWN0IFNpIHbDoCBiZXR3ZWVuIHN1YmplY3QgZWZmZWN0IEZqIG7Dqm4ga2jDtG5nIHBo4bqjaSBi4bqtbiB0w6JtIHbhu4EgZ2nhuqMgxJHhu4tuaCBuw6B5Lg0KDQozKSBQaMawxqFuZyBzYWkgxJHhu5NuZyBuaOG6pXQgOiBN4buXaSBwaMOibiBuaMOzbSAoxJFp4buBdSBraeG7h24gdGjDrSBuZ2hp4buHbSwgYuG6rWMgY+G7p2EgRikgcGjhuqNpIGPDsyBwaMawxqFuZyBzYWkgbmjGsCBuaGF1LiBW4bubaSB0aMOtIG5naGnhu4dtIFJlcGVhdGVkIG1lYXN1cmUsIHRhIGPDsyBnaeG6oyDEkeG7i25oIHF1YW4gdHLhu41uZyBoxqFuIGzDoCDCqyBTcGhlcmljaXR5IMK7LCBoYXkgdMOtbmggxJHhu5NuZyBuaOG6pXQgduG7gSBwaMawxqFuZyBzYWkgY+G7p2EgY8OhYyBnacOhIHRy4buLIGtow6FjIGJp4buHdCDhu58gbeG7l2kgY8OhIHRo4buDICh0cm9uZyAxIGNvdmFyaWFuY2UgbWF0cml4KS4gVGjDrSBk4bulLCBu4bq/dSB0YSBjw7MgSyDEkWnhu4F1IGtp4buHbiB0aMOtIG5naGnhu4dtIChs4bqnbiBs4bq3cCBs4bqhaSksIHRow6wgc+G6vSBjw7MgayhrLTEpLzIgY+G6t3Aga2jDoWMgYmnhu4d0LCBnaeG6oyDEkeG7i25oIHNwaGVyaWNpdHkgbMOgIGNow7puZyDEkeG7gXUgY8OzIHBoxrDGoW5nIHNhaSBuaMawIG5oYXUuDQoNCkdp4bqjIMSR4buLbmggU3BoZXJpY2l0eSBjw7MgdGjhu4Mga2nhu4NtIHRyYSBi4bqxbmcgdGVzdCBNYXVjaGx5LCBob+G6t2MgcXVhbiBzw6F0IGNvdmFyaWFuY2UgbWF0cml4LiBUdXkgbmhpw6puIGLGsOG7m2MgbsOgeSBjw7MgdGjhu4MgYuG7jyBxdWEgbuG6v3UgdGEgaGnhu4d1IGNo4buJbmggZGYgY+G7p2Egd2l0aGluIHN1YmplY3QgZmFjdG9yIChGKSBi4bqxbmcgaOG7hyBz4buRIGVwc2lsb24gdGhlbyBwaMawxqFuZyBwaMOhcCBHcmVlbmhvdXNlLUdlaXNzZXIgKEdHKSBob+G6t2MgSHV5bmgtRmVpbGR0IChIRikuIEVwc2lsb24gPSAxIGtoaSBraMO0bmcgY8OzIHZpIHBo4bqhbSBnaeG6oyDEkeG7i25oDQoNClR1eSBuaGnDqm4sIGdp4bqjIMSR4buLbmggc3BoZXJpY2l0eSBsw6Aga2jDtG5nIGPhuqduIHRoaeG6v3QgKGtow7RuZyB0aOG7gyBi4buLIHZpIHBo4bqhbSksIG7hur91IEs8MyAoY2jhu4kgbOG6t3AgbOG6oWkgdGjDrSBuZ2hp4buHbSAyIGzhuqduKS4gU3BoZXJpY2l0eSBjxaluZyBraMO0bmcgY+G6p24gdGhp4bq/dCBu4bq/dSB0cm9uZyBtw7QgaMOsbmgga2jDtG5nIGPDsyB0xrDGoW5nIHTDoWMgZ2nhu69hIEYgdsOgIFMgKHJhbmRvbSBpbnRlcmNlcHQgbW9kZWwsIGhheSBt4buXaSBjw6EgdGjhu4MgY2jhu4kgxJFvIDEgbOG6p24gdOG6oWkgbeG7l2kgxJFp4buBdSBraeG7h24gSykgdsOsIHZhcmlhbmNlIGPhu6dhIGtow6FjIGJp4buHdCBz4bq9ID0wIG5oxrAgbmhhdSBjaG8gbeG7jWkgY8OhIHRo4buDLiBOaMawIHbhuq15LCB0cm9uZyB0csaw4budbmcgaOG7o3AgaGnhu4duIHThuqFpLCB2aeG7h2MgaGnhu4d1IGNo4buJbmggZGYgdsOgIGtp4buDbSB0cmEgc3BoZXJpY2l0eSBsw6Aga2jDtG5nIGPhuqduIHRoaeG6v3QuDQoNCipQaMawxqFuZyBwaMOhcCBj4buVIMSRaeG7g246IGjDoG0gYW92Kg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnN1bW1hcnkoYW92KEJQY2hhbmdlfnN0ZXArRXJyb3IoQW5pbWFsKSwgZGYpKQ0KDQpgYGANCg0KSMOgbSBhb3YgY2hvIHBow6lwIHRo4buxYyBoaeG7h24gRiB0ZXN0IMSRxqFuIGdp4bqjbiBjaG8gbcO0IGjDrG5oIEJQY2hhbmdlfnN0ZXArRXJyb3IoQW5pbWFsKSANCg0KKkdp4bqjaSBwaMOhcCBNaXhlZCBtb2RlbCoNCg0KTmjhu69uZyBs4bujaSDDrWNoIGtoaSB0aeG6v3AgY+G6rW4gQU5PVkEgY2hvIHRow60gbmdoaeG7h20gbOG6t3AgbOG6oWkgYuG6sW5nIG3DtCBow6xuaCBo4buXbiBo4bujcCAoTWl4ZWQgbW9kZWwpOg0KDQpLaOG6oyBuxINuZyB0w7l5IGJp4bq/biBjYW8gaMahbiBuaGnhu4F1OiBi4bqhbiBjw7MgdGjhu4MgbeG7nyBy4buZbmcgY+G6pXUgdHLDumMgbcO0IGjDrG5oIHRob+G6o2kgbcOhaSwgbGluaCBob+G6oXQgxJHhu4MgdGjDrWNoIG5naGkgduG7m2kgxJHhu5kgcGjhu6ljIHThuqFwIGPhu6dhIHRoaeG6v3Qga+G6vyB0aMOtIG5naGnhu4dtLiBUaMOtIGThu6UgxJHhu4Mga2jhuqNvIHPDoXQgxJHhu5NuZyB0aOG7nWkgbmhp4buBdSBQaMawxqFuZyBwaMOhcCB0cuG7iyBsaeG7h3UsIHTGsMahbmcgdMOhYyB24bubaSB54bq/dSB04buRIFRo4budaSBnaWFuLCBuaOG7r25nIGJp4bq/biBz4buRIGtow6FjLCBwaMOibiBj4bulbSBi4buHbmggbmjDom7igKYgVGjhuq1tIGNow60sIGvhur90IHF14bqjIGPhu6dhIGLhuqFuIGPFqW5nIGtow7RuZyBi4bqvdCBideG7mWMgcGjhuqNpIGzDoCBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyAoYuG6oW4gY8OzIHRo4buDIGThu7FuZyBtaXhlZCBsb2dpc3RpYyBtb2RlbCBsdcO0bikuDQoNCk3DtCBow6xuaCBjaG8gcGjDqXAgYuG6oW4geOG7rSBsw70geeG6v3UgdOG7kSBUaOG7nWkgZ2lhbiBuaMawIG3hu5l0IGJp4bq/biBz4buRIMSR4buLbmggbMaw4bujbmcgbGnDqm4gdOG7pWMgdGjhu7FjIHPhu7EuIEFOT1ZBIGPhu5UgxJFp4buDbiBjaOG7iSBuaMOsbiB0aOG7nWkgxJFp4buDbSBuaMawIGPDoWMgdGjhu6kgYuG6rWMgY+G7p2EgYmnhur9uIMSR4buLbmggdMOtbmguIE1peGVkIG1vZGVsIGPDsyB0aOG7gyBtw7QgaMOsbmggaMOzYSBr4bq/dCBxdeG6oyB0aGVvIHRo4budaSBnaWFuIG3hu5l0IGPDoWNoIGxpw6puIHThu6VjIGNo4bupIGtow7RuZyBjaOG7iSDGsOG7m2MgbMaw4bujbmcgdHJ1bmcgYsOsbmggdOG6oWkgbeG7l2kgdGjhu51pIMSRaeG7g20uIMSQaeG7gXUgbsOgeSBy4bqldCBo4buvdSDDrWNoIG7hur91IHRow60gbmdoaeG7h20gxJHGsOG7o2MgbOG6t3AgbOG6oWkgxJHhu4tuaCBr4buzIHThuqFpIG5oaeG7gXUsIHLhuqV0IG5oaeG7gXUgdGjhu51pIMSRaeG7g20uIMSQaeG7gXUgbsOgeSDEkeG7k25nIG5naMSpYSB24bubaSB2aeG7h2MgVGjhu51pIGdpYW4gY8OzIHRo4buDIGzDoG0gY292YXJpYXRlIChoaeG7h3AgYmnhur9uIHPhu5EpIHRyb25nIHTGsMahbmcgdMOhYyB24bubaSBt4buZdCB54bq/dSB04buRIGtow6FjLg0KDQpQaMawxqFuZyBwaMOhcCBtw7QgaMOsbmggY2hvIHBow6lwIGR1bmcgbuG6oXAgZOG7ryBsaeG7h3UgdHLhu5FuZyAobWlzc2luZyB2YWx1ZSkgdsOgIGPhu6EgbeG6q3UgYuG6pXQgY8OibiB44bupbmcgZ2nhu69hIGPDoWMgcGjDom4gbmjDs20uIFRyb25nIHF1eSB0csOsbmggQU5PVkEgY+G7lSDEkWnhu4NuLCBu4bq/dSBk4buvIGxp4buHdSB0aGnhur91IHPDs3QgdOG6oWkgYuG6pXQgY+G7qSB0aOG7nWkgxJFp4buDbSBuw6BvIHRow6wgdG/DoG4gYuG7mSBk4buvIGxp4buHdSBjw7JuIGzhuqFpIGPhu6dhIGPDoSB0aOG7gyDEkcOzIHPhur0gYuG7iyBsb+G6oWkgYuG7jy4gS2hpIGTDuW5nIG1peGVkIG1vZGVsLCBjaOG7iSB0aOG7nWkgxJFp4buDbSBjw7MgbWlzc2luZyB2YWx1ZSBi4buLIGxv4bqhaSBi4buPLCBjw6FjIGLhuq1jIGdpw6EgdHLhu4sga2jDoWMgY+G7p2EgRmogaGF5IFRqIHbhuqtuIMSRxrDhu6NjIHBow6JuIHTDrWNoLiBNaXhlZCBtb2RlbCB44butIGzDvSBk4buvIGxp4buHdSBkxrDhu5tpIGThuqFuZyDigJxsb25nIGZvcm1hdOKAnSBjaOG7qSBraMO0bmcgcGjhuqNpIMSRYSBiaeG6v24gKHdpZGUgZm9ybWF0KSBuw6puIHPhu5EgbMaw4bujbmcga2jDtG5nIMSR4buTbmcgxJHhu4F1IGdp4buvYSBjw6FjIHBow6JuIG5ow7NtIGtow7RuZyBjw7JuIGzDoCB24bqlbiDEkeG7gSBu4buvYS4NCg0KxJDGoW4gZ2nhuqNuIGjDs2Egdmnhu4djIHRo4buxYyBoaeG7h24gc3V5IGRp4buFbiB0aOG7kW5nIGvDqiwgdGjDrSBk4bulIEYgdGVzdCAodsOsIGzDumMgbsOgeSBNZWFuIG9mIHNxdWFyZXMgY2jhu4kgY8OybiBoYWkgYuG6rWM6IE1vZGVsIHbDoCBSZXNpZHVhbCkuIFThu6sgbcO0IGjDrG5oIGPFqW5nIGThu4UgZMOgbmcga2nhu4NtIMSR4buLbmggw70gbmdoxKlhIHRo4buRbmcga8OqIGPhu6dhIHThu6tuZyBi4buZIHBo4bqtbiBj4bunYSBwaMawxqFuZyBzYWksIGJhbyBn4buTbSBj4bqjIHJhbmRvbSBlZmZlY3RzIHbDoCBjb3ZhcmlhbmNlIG1hdHJpeC4gDQpDdeG7kWkgY8O5bmcsIHTGsCBkdXkgYuG6sW5nIG5nw7RuIG5n4buvIG3DtCBow6xuaCBsw6AgxJFp4buBdSBraeG7h24gcXVhbiB0cuG7jW5nIMSR4buDIGLhuqFuIGLGsOG7m2MgcXVhIHRyxrDhu51uZyBwaMOhaSBCYXllcy4gDQoNClRhIGPDsyB0aOG7gyB0aOG7sWMgaGnhu4duIE1peGVkIG1vZGVsIGLhurFuZyAyIHBhY2thZ2VzIHRyb25nIFI6IG5sbWUgdsOgIGxtZTQNCg0KKm5sbWUgcGFja2FnZSoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCiMgbmxtZSBtZXRob2QNCg0KbGlicmFyeShubG1lKQ0KbW9kMT1sbWUoQlBjaGFuZ2V+c3RlcCwgcmFuZG9tPX4xfEFuaW1hbCwgZGYsbWV0aG9kPSdSRU1MJykNCnN1bW1hcnkobW9kMSkNCmFub3ZhKG1vZDEpDQppbnRlcnZhbHMobW9kMSkNClZhckNvcnIobW9kMSkNCg0KYGBgDQoNCipsbWU0IHBhY2thZ2UqDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KI2xtZTQgbWV0aG9kDQoNCmxpYnJhcnkobG1lNCkNCg0KbW9kMz1sbWVyKEJQY2hhbmdlfnN0ZXAgKyAoMXxBbmltYWwpLCBkZixSRU1MPVRSVUUpDQoNCmxpYnJhcnkobG1lclRlc3QpDQoNCm1vZDM9dXBkYXRlKG1vZDMpDQpzdW1tYXJ5KG1vZDMpDQoNCiMgS2Vud2FyZC1Sb2dlciBkZW5vbWluYXRvciBkZiBtZXRob2QNCg0KYW5vdmEobW9kMyxkZGY9IktlbndhcmQtUm9nZXIiKQ0KYGBgDQoNClRhIGPDsyB0aOG7gyB0aOG6pXkgY+G6oyAzIGvhur90IHF14bqjIGzDoCB0xrDGoW5nIMSRxrDGoW5nLiBUaGVvIMSRw7MsIEYgdGVzdCBjaG8gdGjhuqV5IExp4buBdSB0aHXhu5FjIGPDsyBoaeG7h3Ug4bupbmcgw70gbmdoxKlhIGzDoG0gdMSDbmcgQlBDaGFuZ2UuIChGKDUsMjApPTYwLjEzMywgcF92YWx1ZSA8IDAuMDAwMSkuDQoNCipHaeG6o2kgcGjDoXAgbGFpIHbDoCBob8OgbiBjaOG7iW5oOiBwYWNrYWdlIGFmZXgqDQoNCmFmZXggbMOgIG3hu5l0IHBhY2thZ2UgbeG7m2kgxJHGsOG7o2MgdOG6oW8gcmEgdHJvbmcgbsSDbSAyMDE3LCB2w6AgxJHDonkgbMOgIG3hu5l0IGPDtG5nIGPhu6UgbeG6oW5oIG5o4bqldCBtw6AgTmhpIHThu6tuZyBiaeG6v3QgxJHhu4MgdGjhu7FjIGhp4buHbiBBTk9WQS4gYWZleCBz4butIGThu6VuZyBjw7ogcGjDoXAgxJHGoW4gZ2nhuqNuIHbDoCBo4buXIHRy4bujIHThuqV0IGPhuqMgdGhp4bq/dCBr4bq/IEFOT1ZBIHbhu5tpIGJldHdlZW4sIHdpdGhpbiB2w6AgcmFuZG9tIGVmZmVjdHMsIGNobyBwaMOpcCBoaeG7h3UgY2jhu4luaCB2aSBwaOG6oW0gZ2nhuqMgxJHhu4tuaCBzcGhlcmljaXR5IGLhurFuZyBHRyBoYXkgSEYsIGNobyBwaMOpcCBsw6BtIHBvc3Rob2MgdGVzdHbhu5tpIGhp4buHdSBjaOG7iW5oIG5nxrDhu6FuZyDDvSBuZ2jEqWEgKHRow60gZOG7pSBCb25mZXJyb25pKSB2w6AgY29udHJhc3QgYW5hbHlzaXMgxJHhu4MgY2hvIHJhIGvhur90IHF14bqjIHBob25nIHBow7oga2jDtG5nIHRodWEga8OpbSBuaOG7r25nIHNvZnR3YXJlIHRoxrDGoW5nIG3huqFpIGtow6FjLiBW4bubaSByZXBlYXRlZCBtZXN1cmUgQU5PVkEsIGFmZXggZMO5bmcgZ2nhuqNpIHBow6FwIGxhaSwgdOG7qWMgbMOgIHBo4bqnbiBsw7VpIGPhu6dhIG7DsyBk4buxYSB2w6BvIG1peGVkIG1vZGVsIChwYWNrYWdlIGxtZTQpLCBuaMawbmcgduG6q24geHXhuqV0IHJhIGvhur90IHF14bqjIHRoZW8gdHJ1eeG7gW4gdGjhu5FuZy4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGFmZXgpDQoNCmFvdnQ9YW92X2V6KGlkPSJBbmltYWwiLA0KICAgICAgICAgICAgZHY9IkJQY2hhbmdlIiwNCiAgICAgICAgICAgIGRhdGE9ZGYsDQogICAgICAgICAgICB3aXRoaW49Yygic3RlcCIpLA0KICAgICAgICAgICAgYW5vdmFfdGFibGUgPSBsaXN0KGNvcnJlY3Rpb249IkdHIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcz0icGVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX2FkanVzdF9tZXRob2Q9ImJvbmZlcnJvbmkiKSkNCg0KDQpzdW1tYXJ5KGFvdnQpDQoNCm5pY2UoYW92dCkNCmBgYA0KDQpLaGkga+G6v3QgaOG7o3AgYWZleCB2w6AgcGFja2FnZSBsc21lYW4sIHRhIGPDsyBt4buZdCBnaeG6o2kgcGjDoXAgcuG6pXQgbeG6oW5oIMSR4buDIGzDoG0gcG9zdC1ob2MgdGVzdCB2w6AgY29udHJhc3QgYW5hbHlzaXM6DQoNCmBgYHtyfQ0KbGlicmFyeShsc21lYW5zKQ0KDQpsc209bHNtZWFucyhhb3Z0LHNwZWNzID0gInN0ZXAiKQ0KDQpsc20NCg0KYGBgDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbHNtZGY9bHNtJT4lc3VtbWFyeSgpJT4lYXNfdGliYmxlKCkNCg0KDQpsc21kZiU+JWdncGxvdChhZXMoeD1zdGVwLCANCiAgICAgICAgICAgICAgICAgICB5PWxzbWVhbiwNCiAgICAgICAgICAgICAgICAgICBmaWxsPXN0ZXAsDQogICAgICAgICAgICAgICAgICAgZ3JvdXA9MSkpKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWxzbWVhbi1TRSwgDQogICAgICAgICAgICAgICAgICAgIHltYXg9bHNtZWFuK1NFKSwgDQogICAgICAgICAgICAgICAgd2lkdGg9MC4yLHNpemU9MSkgKw0KICBnZW9tX2xpbmUoc2l6ZT0xLGNvbD0icmVkNCIpKw0KICBnZW9tX3BvaW50KHNpemU9NSxzaGFwZT0yMSxjb2w9ImJsYWNrIikrDQogIHNjYWxlX3lfY29udGludW91cygiQlBjaGFuZ2UiKSsNCiAgdGhlbWVfYncoKQ0KDQpgYGANCg0KVGjDrSBk4bulLCBt4buZdCBwaMOibiB0w61jaCB0xrDGoW5nIHBo4bqjbiB24bubaSAyIGdp4bqjIHRodXnhur90IEgwIDogVHJ1bmcgYsOsbmggQlBjaGFuZ2UgY+G7p2EgMyBsaeG7gXUgRDEyMyB0xrDGoW5nIMSRxrDGoW5nIHbhu5tpIGPhu6dhIEQ0NTYsIHbDoDogVHJ1bmcgYsOsbmggQlBDaGFuZ2UgbMOgIG5oxrAgbmhhdSBnaeG7r2EgMyBuaMOzbSBsaeG7gXU6IEQxMiwgRDM0IHbDoCBENTY6DQoNClRhIGNo4buJIGPhuqduIMSRxrBhIHRy4buNbmcgc+G7kSBjb250cmFzdCB2w6BvIG1hdHJpeCB2w6AgZMO5bmcgaMOgbSBjb250cmFzdCB0csOqbiBvYmplY3QgbHNtOg0KDQpgYGB7cn0NCmNvbnRyYXN0KGxzbSwgDQogICAgICAgICBsaXN0KCJEMTIzL0Q0NTYiPWMoLTEsLTEsLTEsMSwxLDEpLzMsDQogICAgICAgICAgICAgICJEMTIvRDM0L0Q1NiI9YygtMiwtMiwtMSwtMSwxLDEpLzMpLA0KICAgICAgICAgYWRqdXN0PSJib25mZXJyb25pIikNCmBgYA0KDQpL4bq/dCBxdeG6oyBiw6FjIGLhu48gY+G6oyAyIGdp4bqjIHRodXnhur90IEgwIG7DqnUgdHLDqm4sIG5oxrAgduG6rXkgY8OzIHTGsMahbmcgcGjhuqNuIMO9IG5naMSpYSBnaeG7r2EgbmjDs20gbGnhu4F1IHRo4bqlcCB2w6AgbGnhu4F1IGNhbywgZ2nhu69hIDMgbmjDs20gbGnhu4F1IFRo4bqlcCwgVHJ1bmcgYsOsbmggdsOgIENhby4NCg0KVGEgY8WpbmcgY8OzIHRo4buDIGzDoG0gdGVzdCBDb250cmFzdCBow6BtIMSRYSB0aOG7qWMgYuG6rWMgY2FvOg0KDQpgYGB7cn0NCg0KY29udHJhc3QobHNtLG1ldGhvZD0icG9seSIpDQoNCnBjZGY9Y29udHJhc3QobHNtLG1ldGhvZD0icG9seSIpJT4lY29lZigpJT4lYXNfdGliYmxlKCkNCg0KY29sbmFtZXMocGNkZik9YygiU3RlcCIsImxpbmVhciIsIlF1YWRyYXRpYyIsIkN1YmljIiwiUXVhcnRpYyIsIkRlZy41IikNCg0KcGNkZiU+JWdhdGhlcihsaW5lYXI6RGVnLjUsa2V5PSJQb2x5Iix2YWx1ZT0iQ29lZiIpJT4lDQogIGdncGxvdChhZXMoeD1TdGVwLHk9UG9seSxmaWxsPUNvZWYpKSsNCiAgZ2VvbV90aWxlKGNvbD0iYmxhY2siKStzY2FsZV9maWxsX2dyYWRpZW50Mihsb3c9ImJsdWUiLGhpZ2g9InJlZCIsbWlkPSJ3aGl0ZSIpKw0KICB0aGVtZV9idygpDQoNCnBjZGYlPiVnYXRoZXIobGluZWFyOkRlZy41LGtleT0iUG9seSIsdmFsdWU9IkNvZWYiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9U3RlcCx5PUNvZWYsY29sPVBvbHksZ3JvdXA9MSkpKw0KICBnZW9tX3BhdGgoKStmYWNldF93cmFwKH5Qb2x5LG5jb2w9MikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpL4bq/dCBxdeG6oyBQb2x5bm9taWFsIGNvbnRyYXN0IGFuYWx5c2lzIGNobyB0aOG6pXkga2h1eW5oIGjGsOG7m25nIHRoYXkgxJHhu5VpIGPhu6dhIEJQY2hhbmdlIGPDsyB0aOG7gyDEkcaw4bujYyB44bqlcCB44buJIGLhurFuZyAxIGjDoG0gYuG6rWMgMyAoY3ViaWMpIG5naOG7i2NoIGJp4bq/biwgaG/hurdjIGLhuq1jIDEgKHR1eeG6v24gdMOtbmgpLsSQaeG7gXUgbsOgeSBjw7MgbmdoxKlhIGfDrDogSHV54bq/dCDDoXAgdMSDbmcgZOG6p24gdGhlbyBsaeG7gXUgdGh14buRYyAoaMOgbSBsaW5lYXIpOyB0dXkgbmhpw6puIOG7nyBuaOG7r25nIGxp4buBdSB0aOG6pXAgbmjhuqV0LCBodXnhur90IMOhcCBjaOG7iSB0xINuZyBuaOG6uSwgdOG6oWkgbeG7mXQgbGnhu4F1IHRydW5nIGdpYW4gbsOgbyDEkcOzLCBz4bq9IHTEg25nIMSR4buZdCBiaeG6v24sIHNhdSDEkcOzIHPhur0gZHV5IHRyw6wgcGhhIGLDrG5oIG5ndXnDqm4g4bufIG5o4buvbmcgbGnhu4F1IGNhbyBoxqFuIChow6BtIGN1YmljKQ0KDQpN4buZdCBsb+G6oWkgY29udHJhc3QgYW5hbHlzaXMga2jDoWMgY8OzIHRo4buDIHRo4butLCDEkcOzIGzDoCByZXZlcnNlZCBwYWlyd2lzZSAoYuG6r3QgY+G6t3AgdHXhuqduIHThu7Egbmdo4buLY2ggY2hp4buBdSksIHThu6ljIGzDoCBzbyBzw6FuaCBnaeG7r2EgbGnhu4F1IHNhdSB2w6AgbGnhu4F1IHRo4bqlcCBoxqFuOjoNCg0KYGBge3J9DQpjb250cmFzdChsc20sInJldnBhaXJ3aXNlIixhZGp1c3Q9ImJvbmZlcnJvbmkiKQ0KDQpjZGY9Y29udHJhc3QobHNtLCJyZXZwYWlyd2lzZSIsYWRqdXN0PSJib25mZXJyb25pIiklPiUNCiAgY29lZigpJT4lDQogIGFzX3RpYmJsZSgpDQoNCmNkZiU+JWdhdGhlcihjLjE6Yy4xNSxrZXk9IlBhaXJzIix2YWx1ZT0iQ29lZiIpJT4lDQogIGdncGxvdChhZXMoeD1zdGVwLHk9UGFpcnMsZmlsbD1hcy5mYWN0b3IoQ29lZikpKSsNCiAgZ2VvbV90aWxlKGNvbD0iYmxhY2siKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiYmx1ZSIsIndoaXRlIiwicmVkIikpKw0KICB0aGVtZV9idygpDQpgYGANCg0KS+G6v3QgcXXhuqMgY2hvIHRo4bqleSBENCBjaMOtbmggbMOgIGxp4buBdSBrw61jaCBob+G6oXQgc+G7sSB0xINuZyBodXnhur90IMOhcCDEkeG7mXQgYmnhur9uLCBzbyB24bubaSAzIGxp4buBdSB0csaw4bubYyDEkcOzLCBzYXUgxJHDsyBodXnhur90IMOhcCB0xINuZyBk4bqnbiB04burIEQ0LUQ1IG5oxrBuZyBi4bqvdCDEkeG6p3UgZ2nhuqNtIHThu6sgRDUgxJHhur9uIEQ2Lg0KDQojIEdp4bqjaSBwaMOhcCBCYXllcyBjaG8gdGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaQ0KDQpUcm9uZyBtw7QgaMOsbmggU1RBTiwgY2jDum5nIHRhIHBow6JuIHTDrWNoIGLDoGkgdG/DoW4gaGnhu4duIHRo4budaSB0aGVvIHRo4bupIGLhuq1jIG5oxrAgc2F1Og0KDQorIEdpw6EgdHLhu4sgY+G7p2EgWSAoQlBjaGFuZ2UpIMSRxrDhu6NjIG3DtCB04bqjIGLhurFuZyBwaMOibiBwaOG7kWkgR2F1c3NpYW4gKG5vcm1hbCkgduG7m2kgdGhhbSBz4buRIDogduG7iyB0csOtIHRydW5nIHTDom0gTXUgdsOgIHNjYWxlIFNpZ21hLCDEkcOieSBsw6AgaMOgbSBsaWtlbGlob29kIGPhu6dhIFNUQU4gbW9kZWwNCg0KKyBHacOhIHRy4buLIE11W2lqXSDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgYuG6sW5nIG3hu5l0IG3DtCBow6xuaCA6DQoNCiQkXG11IF97aWp9ID0gXGJldGEgX3tqfS5YX3tqfSArIFxnYW1tYV97aX0kJA0KDQojIE3DtCBow6xuaCBCYXllcyB0cm9uZyBTVEFODQoNCiFbXShyZXBBTk9WQUJheWVzMy5wbmcpDQoNClRyb25nIMSRw7MsIEZpeGVkIGVmZmVjdCDigJMgaGnhu4d1IOG7qW5nIHRow60gbmdoaeG7h20gxJHGsOG7o2MgxrDhu5tjIHTDrW5oIG5oxrAgdMOtY2ggY+G7p2EgNiB0aGFtIHPhu5EgYmV0YShqKSB24bubaSBqID0xLDIsMyw0LDUsNiB2w6AgZGVzaWduIG1hdHJpeCBYaiB24bubaSA2IHByZWRpY3RvcnMgKEQxLEQyLEQzLEQ0LEQ1LEQ2KTsgUmFuZG9tIGVmZmVjdCDigJMgYmnhur9uIHRoacOqbiBuZ+G6q3Ugbmhpw6puIGPDoSB0aOG7gyDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgbmjGsCA1IHRoYW0gc+G7kSBnYW1tYShpKSB0xrDGoW5nIOG7qW5nIHbhu5tpIDUgY29uIHRo4buPIHRyb25nIHRow60gbmdoaeG7h20NCg0KKyBNw7QgaMOsbmggdMaw4budbmcgbWluaCBjw7MgbuG7mWkgZHVuZzoNCg0KJCRcbXUgPSBcYmV0YSBfezF9RDEgKyBcYmV0YSBfezJ9RDIgKyBcYmV0YSBfezN9RDMgKyBcYmV0YSBfezR9RDQgKyBcYmV0YSBfezV9RDUgKyBcYmV0YSBfezZ9RDYgKyBcZ2FtbWFfe2l9JCQNCg0KKyBN4bq3dCBraMOhYywgdGEgZ2nhuqMgxJHhu4tuaCBy4bqxbmcgZ2FtbWEgY8OzIHBow6JuIHBo4buRaSBjaHXhuqluIHbhu5tpIDEgdGhhbSBz4buRIHNjYWxlIGzDoCBTaWdtYV9CDQoNCisgTeG7pWMgdGnDqnUgY+G7p2EgY2jDum5nIHRhIGzDoCB0w6xtIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIDogNiB0aGFtIHPhu5EgYmV0YSwgdGhhbSBz4buRIHNpZ21hLCA1IHRoYW0gc+G7kSBnYW1tYSB2w6Agc2lnbWFfQg0KDQorIFbhu4EgUHJpb3IgKHRp4buBbiDEkeG7i25oKSwgdGEgY8OzIHRo4buDIGdp4bqjIMSR4buLbmggbmjGsCBzYXU6IGJldGEgY8OzIHBow6JuIG5vcm1hbCAoMCwxMDApOyBnYW1tYSBjw7MgcGjDom4gcGjhu5FpIG5vcm1hbCgwLHNpZ21hX0IpLCBzaWdtYV9CIHbDoCBzaWdtYSBjw7MgcGjDom4gcGjhu5FpIGhhbGYgQ2F1Y2h5DQoNCisgROG7ryBsaeG7h3UgxJHhuqd1IHbDoG8gY+G7p2EgbcO0IGjDrG5oIFNUQU4gZ+G7k20gY8OzOg0KDQoxKSBD4buhIG3huqt1IG49IMSR4buZIGTDoGkgbWF0cml4IGxvbmcgZGF0YSAobj0zMCkNCg0KMikgU+G7kSBkdW1teSB2YXJpYWJsZXMgdHJvbmcgZGVzaWduIG1hdHJpeCwgaGF5IGLhuq1jIGsgY+G7p2EgeeG6v3UgdOG7kSBjYW4gdGhp4buHcCB0cm9uZyB0aMOtIG5naGnhu4dtLCBrPTYgdsOsIGPDsyA2IGxp4buBdSB0aHXhu5FjIChtw7QgaMOsbmggbsOgeSBraMO0bmcgY2jhu6lhIEludGVyY2VwdCwgbeG7mXQgcGjhuqduIEludGVyY2VwdCDEkcOjIG7hurFtIHRyb25nIHRoYW0gc+G7kSBnYW1tYXMpLg0KDQozKSBuQiA9IHPhu5EgbMaw4bujbmcgY8OhIHRo4buDLCDhu58gxJHDonkgbkI9NQ0KDQo0KSBWZWN0b3IgYmnhur9uIGvhur90IHF14bqjIFksIGNow61uaCBsw6AgQlBjaGFuZ2UNCg0KNSkgRGVzaWduIG1hdHJpeCBYIGNo4bupYSBjw6FjIGR1bW15IHZhcmlhYmxlICB04bqhbyByYSB04burIDYgYuG6rWMgeeG6v3UgdOG7kSBjYW4gdGhp4buHcCBsw6AgZG9zZSBzdGVwDQoNCjYpIFZlY3RvciBz4buRIG5ndXnDqm4gQiwgbMOgIGPhu5l0IG3DoyBz4buRIMSR4buLbmggZGFuaCBjaG8gbeG7l2kgY8OhIHRo4buDIHRyb25nIGxvbmcgZGF0YSwgbMawdSDDvSA6IHZlY3RvciBuw6B5IGPDsyDEkeG7mSBkw6BpID0gbiBjaOG7qSBraMO0bmcgcGjhuqNpIG5CDQoNClRhIHZp4bq/dCBj4bqldSB0csO6YyBtw7QgaMOsbmggbsOgeSB2w6BvIFNUQU4gY29kZSA6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KIyBTVEFODQoNCnJzdGFuU3RyaW5nPSINCmRhdGF7DQppbnQgbjsgLy9jbyBtYXUNCmludCBuWDsgLy9wcmVkaWN0b3JzDQppbnQgbkI7IC8vIHNvIGJsb2NrDQp2ZWN0b3IgW25dIHk7ICAvLyBvdXRjb21lDQptYXRyaXggW24sblhdIFg7IC8vIGRlc2lnbiBtYXRyaXgNCmludCBCW25dOyAvL2Jsb2NrIHZlY3Rvcg0KfQ0KDQpwYXJhbWV0ZXJzew0KdmVjdG9yIFtuWF0gYmV0YTsgLy8gZml4ZWQgZWZmZWN0DQpyZWFsIHNpZ21hOyANCnZlY3RvciBbbkJdIGdhbW1hOyAvLyByYW5kb20gZWZmZWN0DQpyZWFsIHNpZ21hX0I7DQp9DQoNCnRyYW5zZm9ybWVkIHBhcmFtZXRlcnMgew0KdmVjdG9yW25dIG11OyAgLy8gZml4ZWQgZWZmZWN0DQptdSA9IFgqYmV0YTsNCmZvciAoaSBpbiAxOm4pIHsNCm11W2ldPSBtdVtpXSArIGdhbW1hW0JbaV1dOyAgLy9tZWFuIHByZWRpY3RlZA0KfQ0KDQp9IA0KbW9kZWx7DQovLyBQcmlvcnMNCmJldGEgfiBub3JtYWwoMCwxMDApOw0KZ2FtbWEgfiBub3JtYWwoMCxzaWdtYV9CKTsNCnNpZ21hX0IgfiBjYXVjaHkoMCwyNSk7DQpzaWdtYSB+IGNhdWNoeSgwLDI1KTsNCg0KLy9saWtlbGlob29kDQp5IH4gbm9ybWFsKCBtdSAsIHNpZ21hICk7DQp9DQoiDQoNCmBgYA0KDQpTYXUga2hpIGNvZGUgbcO0IGjDrG5oLCB0YSBz4bq9IHThuqFvIHJhIGThu68gbGnhu4d1IMSR4bqndSB2w6BvIGNobyBwaMOibiB0w61jaCBCYXllcywgZMaw4bubaSBk4bqhbmcgMSBsaXN0LCB0cm9uZyDEkcOzIHRhIGtoYWkgYsOhbyBjw6FjIHRow6BuaCBwaOG6p24gbcOgIGJsb2NrIGRhdGEgxJHDoyBtw7QgdOG6oy4NCg0KYGBge3J9DQpYbWF0PW1vZGVsLm1hdHJpeCh+c3RlcCwgZGF0YT1kZikNCg0KZGF0YWxpc3Q9d2l0aChkZiwgbGlzdCh5PUJQY2hhbmdlLCBYPVhtYXQsIG5YPW5jb2woWG1hdCksDQogICAgICAgICAgICAgICAgICAgICAgIEI9YXMuaW50ZWdlcihBbmltYWwpLA0KICAgICAgICAgICAgICAgICAgICAgICBuPW5yb3coZGYpLCANCiAgICAgICAgICAgICAgICAgICAgICAgbkI9bGVuZ3RoKGxldmVscyhBbmltYWwpKSkpDQpgYGANCg0KVGEgxJHGsGEgZGF0YSB2w6BvIG3DtCBow6xuaCB2w6Aga8OtY2ggaG/huqF0IHF1eSB0csOsbmggbOG6pXkgbeG6q3UgTUNNQyB24bubaSBjw6FjIHTDuXkgY2jhu4luaCBUYSBz4bq9IGNo4bqheSA0IGNodeG7l2kgc29uZyBzb25nOiBUcsaw4bubYyBo4bq/dCBzYW1wbGVyIHPhur0gY2jhuqF5IDEwMDAgbMaw4bujdCBraOG7n2kgxJHhu5luZywga2hpIOG7lW4gxJHhu4tuaCB0aMOsIGvhur90IHF14bqjIE1DTUMgYuG6r3QgxJHhuqd1IMSRxrDhu6NjIGdoaSBs4bqhaSAyMDAwIGzGsOG7o3QgY2hvIG3hu5dpIGNodeG7l2kgc2F1IMSRw7MgcsO6dCBn4buNbiAxLzIgxJHhu4MgY3Xhu5FpIGPDuW5nIGNo4buJIGPDsm4gMTAwMCBnacOhIHRy4buLIHggNCBjaHXhu5dpID0gNDAwMCBnacOhIHRy4buLIGNobyBt4buXaSB0aGFtIHPhu5EsIHbDoCBjaOG6oXkgc29uZyBzb25nIHRyw6puIHThu5FpIMSRYSA0IGNvcmVzIGPhu6dhIG3DoXkgdMOtbmguIA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkocnN0YW4pDQoNCnJzdGFuX29wdGlvbnMoYXV0b193cml0ZSA9IFRSVUUpDQpvcHRpb25zKG1jLmNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkpDQoNCnNldC5zZWVkKDEyMykNCg0KZml0IDwtIHN0YW4oZGF0YT1kYXRhbGlzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2NvZGU9cnN0YW5TdHJpbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFpbnM9NCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZXI9MzAwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHdhcm11cD0xMDAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpbj0yLA0KICAgICAgICAgICAgY29udHJvbD1saXN0KGFkYXB0X2RlbHRhPTAuOTkpKQ0KYGBgDQoNClF1eSB0csOsbmggY29udmVyZ2VkIGPhu7FjIG5oYW5oIHbDrCBk4buvIGxp4buHdSBjw7MgbiB0aOG6pXAuIELDonkgZ2nhu50gdGEgY8OzIDEgb2JqZWN0IHTGsMahbmcgxJHhu5FpIGzhu5tuLCBjaOG7qWEgNCBjaHXhu5dpIE1DTUN4MTAwMCBsxrDhu6N0IGNobyA2IHRoYW0gc+G7kSBiZXRhLCBzaWdtYSwgc2lnbWFfQiwgNSB0aGFtIHPhu5EgZ2FtbWEsIHbDoCAzMCBnacOhIHRy4buLIMaw4bubYyBsxrDhu6NuZyBjaG8gTXUuDQoNCiMgUGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY2hvIEZpeGVkIHbDoCBSYW5kb20gZWZmZWN0cw0KDQpWw6wgdGEgxJFhbmcgY8OzIHRyb25nIHRheSBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSB04bqldCBj4bqjIHRoYW0gc+G7kSAodGjDoG5oIHBo4bqnbikgY+G7p2EgbcO0IGjDrG5oLCB0YSBjw7MgdGjhu4MgbOG6p24gbMaw4bujdCBraGFpIHRow6FjIGNow7puZywgYuG6r3QgxJHhuqd1IGLhurFuZyBGaXhlZCBlZmZlY3QgKGhp4buHdSDhu6luZyBjaMOtbmgpDQoNCipGaXhlZCBlZmZlY3QqDQoNClBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIDYgdGhhbSBz4buRIGJldGEgKGNow61uaCBsw6AgdHJ1bmcgYsOsbmggY+G7p2EgQlBjaGFuZ2UgY2hvIDYgYuG6rWMgY+G7p2EgZG9zZXN0ZXAsIGhheSBmaXhlZCBlZmZlY3QgY+G7p2EgZG9zZSBzdGVwIHNhdSBraGkgaGnhu4d1IGNo4buJbmggY2hvIHJhbmRvbSBlZmZlY3QpIGPDsyB0aOG7gyDEkcaw4bujYyBraOG6o28gc8OhdCBuaMawIHNhdSA6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcG9zdGRmPWFzLmRhdGEuZnJhbWUoZml0KSU+JWFzX3RpYmJsZSgpDQoNCmJldGFkZj1wb3N0ZGZbLGMoMTo2KV0lPiVtdXRhdGUoLixJdGVyYXRpb249YXMubnVtZXJpYyhyZXAoYygxOjEwMDApLDQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDaGFpbj1hcy5mYWN0b3IocmVwKGMoMTo0KSxlYWNoPTEwMDApKSkNCg0KY29sbmFtZXMoYmV0YWRmKT1jKCJEMSIsIkQyIiwiRDMiLCJENCIsIkQ1IiwiRDYiLCJJdGVyYXRpb24iLCJDaGFpbiIpDQoNCmhlYWQoYmV0YWRmKSU+JS5bLGMoMTo2KV0lPiVrbml0cjo6a2FibGUoKQ0KDQpwMT1iZXRhZGYlPiVnYXRoZXIoRDE6RDYsa2V5PSJTdGVwIix2YWx1ZT0iRml4ZWRfZWZmZWN0IiklPiUNCiAgZ2dwbG90KGFlcyh4PUZpeGVkX2VmZmVjdCxmaWxsPUNoYWluLGNvbD1DaGFpbikpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4zLHNob3cubGVnZW5kID0gRikrDQogIGZhY2V0X3dyYXAoflN0ZXAsbmNvbD0xLHNjYWxlcyA9ICJmcmVlX3kiKSsNCiAgdGhlbWVfYncoNikNCiAgDQpwMj1iZXRhZGYlPiVnYXRoZXIoRDE6RDYsa2V5PSJTdGVwIix2YWx1ZT0iRml4ZWRfZWZmZWN0IiklPiUNCiAgZ2dwbG90KGFlcyh5PUZpeGVkX2VmZmVjdCx4PUl0ZXJhdGlvbixjb2w9Q2hhaW4pKSsNCiAgZ2VvbV9wYXRoKGFscGhhPTAuNSxzaG93LmxlZ2VuZCA9IEYpKw0KICBmYWNldF93cmFwKH5TdGVwLG5jb2w9MSxzY2FsZXMgPSAiZnJlZSIpKw0KICB0aGVtZV9idyg2KQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMixwMSxuY29sPTIpDQpgYGANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQojIFBsb3QNCg0KYmV0YWRmJHBzZXVkb0dyb3VwPWZhY3RvcihyZXAoYygxOjIwKSxlPW5yb3coYmV0YWRmKS8yMCkpDQoNCmJldGFkZiU+JWdhdGhlcihEMTpENixrZXk9IlN0ZXAiLHZhbHVlPSJGaXhlZF9lZmZlY3QiKSU+JQ0KZ2dwbG90KCkrDQogIGdlb21fam95KGFlcyh5PVN0ZXAsDQogICAgICAgICAgICAgIHg9Rml4ZWRfZWZmZWN0LA0KICAgICAgICAgICAgICBjb2w9cHNldWRvR3JvdXAsDQogICAgICAgICAgICAgIGZpbGw9U3RlcCksDQogICAgICAgICAgIGFscGhhPTAuMDMsc2hvdy5sZWdlbmQgPSBGLHNjYWxlPTEpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9bWVkaWFuKGRmJEJQY2hhbmdlKSxjb2w9InJlZDMiLGxpbmV0eXBlPTIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoLTIwLC0xMCwwLDEwLDIwLDMwLDQwLDUwKSkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlB1UmQiKSsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmVkcyIpKw0KICB0aGVtZV9idygpDQoNCmBgYA0KDQpUaeG6v3AgdGhlbywgdGEgc+G6vSBraOG6o28gc8OhdCBSYW5kb20gZWZmZWN0cywgaGF5IDUgdGhhbSBz4buRIGdhbW1hcyBjaG8gNSBjb24gdGjhu486IA0KDQpHYW1tYXMgY2hvIGJp4bq/dCBzYWkgYmnhu4d0IG5n4bqrdSBuaGnDqm4sIGPGoSBi4bqjbiB2w6AgY8OhIHRo4buDIGPhu6dhIHThu6tuZyBjb24gdGjhu48gYsOqbiBuZ2/DoGkgaGnhu4d1IOG7qW5nIGNow61uaCBj4bunYSA2IGxp4buBdSB0aHXhu5FjDQoNCioqUmFuZG9tIGVmZmVjdHMqKg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCiMgUmFuZG9tIGVmZmVjdHMgKGdhbW1hcykNCg0KZ2FtbWFkZj1wb3N0ZGZbLGMoODoxMildJT4lbXV0YXRlKC4sSXRlcmF0aW9uPWFzLm51bWVyaWMocmVwKGMoMToxMDAwKSw0KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENoYWluPWFzLmZhY3RvcihyZXAoYygxOjQpLGVhY2g9MTAwMCkpKQ0KDQpjb2xuYW1lcyhnYW1tYWRmKT1jKCJSMSIsIlIyIiwiUjMiLCJSNCIsIlI1IiwiSXRlcmF0aW9uIiwiQ2hhaW4iKQ0KDQpnYW1tYWRmJHBzZXVkb0dyb3VwPWZhY3RvcihyZXAoYygxOjIwKSxlPW5yb3coZ2FtbWFkZikvMjApKQ0KDQpnYW1tYWRmJT4lZ2F0aGVyKFIxOlI1LGtleT0iQmxvY2tzIix2YWx1ZT0iUmFuZG9tX2VmZmVjdCIpJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2pveShhZXMoeT1CbG9ja3MsDQogICAgICAgICAgICAgICB4PVJhbmRvbV9lZmZlY3QsDQogICAgICAgICAgICAgICBjb2w9cHNldWRvR3JvdXAsDQogICAgICAgICAgICAgICBmaWxsPUJsb2NrcyksDQogICAgICAgICAgIGFscGhhPTAuMDMsc2hvdy5sZWdlbmQgPSBGLHNjYWxlPTEpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCxjb2w9ImJsdWUzIixsaW5ldHlwZT0yKSsNCiAgY29vcmRfZmxpcCgpKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiQmx1ZXMiKSsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iQmx1ZXMiKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNClRhIHRo4bqtbSBjaMOtIGPDsyB0aOG7gyBraOG6o28gc8OhdCBwaMOibiBwaOG7kWkgaOG6rXUgbmdoaeG7h20gY2hvIE11IChZaGF0KSBj4bunYSA1IGNvbiB0aOG7jyB04bqhaSB04burbmcgYuG6rWMgY+G7p2EgZG9zZXN0ZXA6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQojIFlfaGF0DQoNCnloYXRkZj1wb3N0ZGZbLGMoMTQ6NDMpXSU+JW11dGF0ZSguLEl0ZXJhdGlvbj1hcy5udW1lcmljKHJlcChjKDE6MTAwMCksNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENoYWluPWFzLmZhY3RvcihyZXAoYygxOjQpLGVhY2g9MTAwMCkpKQ0KDQp5aGF0ZGYkcHNldWRvR3JvdXA9ZmFjdG9yKHJlcChjKDE6MjApLGU9bnJvdyh5aGF0ZGYpLzIwKSkNCg0KeWhhdGRmJT4lZ2F0aGVyKGBtdVsxXWA6YG11WzMwXWAsa2V5PSJPYnNlcnZhdGlvbiIsdmFsdWU9IllfaGF0IiklPiUNCiAgbXV0YXRlKC4sU3RlcD1yZXAoYygiRDEiLCJEMiIsIkQzIiwiRDQiLCJENSIsIkQ2IiksZWFjaD0yMDAwMCkpJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2pveShhZXMoeT1PYnNlcnZhdGlvbiwNCiAgICAgICAgICAgICAgIHg9WV9oYXQsDQogICAgICAgICAgICAgICBjb2w9cHNldWRvR3JvdXAsDQogICAgICAgICAgICAgICBmaWxsPVN0ZXApLA0KICAgICAgICAgICBhbHBoYT0wLjAyLHNob3cubGVnZW5kID0gRixzY2FsZT0xKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PW1lZGlhbihkZiRCUGNoYW5nZSksY29sPSJibHVlMyIsbGluZXR5cGU9MikrDQogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1ULG9wdGlvbj0iQyIsZGlyZWN0aW9uID0gLTEpKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUmRQdSIpKw0KICB0aGVtZV9idygpKw0KICBmYWNldF93cmFwKH5TdGVwLG5jb2w9MixzY2FsZXM9ImZyZWVfeSIpDQoNCg0KYGBgDQoNCiMgQuG6o25nIEFOT1ZBIHThu6sgbcO0IGjDrG5oIEJBWUVTDQoNCk7hu5lpIGR1bmcgY+G7p2EgYsaw4bubYyBuw6B5IGzDoCB0xrDGoW5nIHThu7EgbmjGsCBjw6FjaCB0YSBsw6BtIMSR4buDIGThu7FuZyBi4bqjbmcgQU5PVkEgY+G7lSDEkWnhu4NuLCBjaOG7iSBraMOhYyDEkcOzIGzDoCBNU00gdsOgIE1TUiB2w6AgY8OhYyBzdW0gb2Ygc3F1YXJlcyBraMO0bmcgxJHGsOG7o2MgeMOhYyDEkeG7i25oIHThu6sgbcO0IGjDrG5oIHR1eeG6v24gdMOtbmggZMO5bmcgYWxnb3JpdGhtIExlYXN0IHNxdWFyZSBoYXkgUkVNTCwgbcOgIGTDuW5nIGNow61uaCBtw7QgaMOsbmggQmF5ZXMgduG7m2kgNiB0aGFtIHPhu5EgQmV0YSAoZmlleCBlZmZlY3QpIHbDoCA1IHRoYW0gc+G7kSBnYW1tYSAocmFuZG9tIGVmZmVjdCkuIMSQ4buDIMSRxqFuIGdp4bqjbiwgdGEgbOG6pXkgVHJ1bmcgduG7iyBj4bunYSBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSBt4buXaSB0aGFtIHPhu5EgxJHhu4MgdOG6oW8gcmEgMSBtw7QgaMOsbmggxJHhuqFpIGRp4buHbi4NCg0KxJDhuqd1IHRpw6puLCB0YSB0w61uaCBZX2hhdCB04burIG3DtCBow6xuaCDEkeG6oWkgZGnhu4duIG5oxrAgc2F1Og0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCg0KY29lZnM9YXMubWF0cml4KGZpdCklPiUuWyxjKDE6NildJT4lYXBwbHkoLiwyLCBtZWRpYW4pDQoNCmdhbXM9YXMubWF0cml4KGZpdCklPiUuWyxjKDg6MTIpXSU+JWFwcGx5KC4sMiwgbWVkaWFuKQ0KZ2FtPWMocmVwKGdhbXNbWzFdXSw2KSwNCiAgICAgIHJlcChnYW1zW1syXV0sNiksDQogICAgICByZXAoZ2Ftc1tbM11dLDYpLA0KICAgICAgcmVwKGdhbXNbWzRdXSw2KSwNCiAgICAgIHJlcChnYW1zW1s1XV0sNikNCiAgICAgICkNCg0KcHJlZERGPWRhdGEuZnJhbWUoeD1kZiRzdGVwLHRydXRoPWRmJEJQY2hhbmdlKQ0KDQpZaGF0PShjb2VmcyAlKiUgdChtb2RlbC5tYXRyaXgofnByZWRERiR4KSkrZ2FtKSU+JWFzLnZlY3RvcigpDQoNCmBgYA0KDQpTYXUgxJHDsywgdOG7qyBtw7QgaMOsbmggxJHhuqFpIGRp4buHbiBCYXllcyBuw6B5LCB0YSB0w61uaCBjw6FjIFNTLCBNU00sIE1TUiwgRiB2YWx1ZXMgY8WpbmcgbmjGsCBjw6FjIGVmZmVjdC1zaXplcy4NCg0KR2hpIGNow7o6IELhuq1jIHThu7EgZG8gY+G7p2EgY8OhYyB0aMOgbmggcGjhuqduIMSRxrDhu6NjIHTDrW5oIG5oxrAgc2F1Og0KDQokJGRmX3t0b3RhbH09IChOLTEpJCQNCg0KJCRkZl97Zml4fT0gKGstMSkkJA0KJCRkZl97cmFuZG9tfT0gKHMtMSkkJA0KDQokJGRmX3tyZXNpZHVhbH0gPSAoTi1rLXMrMSkkJA0K4bueIMSRw6J5LCBEb3NlIHN0ZXAgbMOgIGs9NiBkbyBjw7MgNiBi4bqtYyBnacOhIHRy4buLIGPhu6dhIERvc2VzdGVwLCBzPTUgY2hvIDUgY29uIHRo4buPLCBOPTMwLg0KDQpUYSBi4bqvdCDEkeG6p3UgdMOtbmggdGjhu6cgY8O0bmcgbXNtLCBtc3IsIEYsIHBfdmFsdWUgY2hvIEYgdGVzdCB2w6AgZWZmZWN0c2l6ZXMgbMOgIGV0YTIgdsOgIG9tZWdhMjogDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGYxPTUNCmRmMj0zMC01LTYrMQ0Kbj0zMA0Kaz02DQoNCnNzbT1zdW0oIChZaGF0LW1lYW4ocHJlZERGJHRydXRoKSleMiApDQpzc3I9c3VtKCAocHJlZERGJHRydXRoLVloYXQpXjIgKQ0Kc3N0PXN1bSggKHByZWRERiR0cnV0aC1tZWFuKHByZWRERiR0cnV0aCkpXjIgKQ0KbXNtPXNzbS9kZjENCm1zcj1zc3IvZGYyDQpmPW1zbS9tc3INCg0KZXRhMj1zc20vc3N0DQpyPXNxcnQoc3NtL3NzdCkNClIyPXJeMg0Kb21lZ2EyPShzc20tZGYxKm1zcikvKHNzdCttc3IpICMgb3IgKChrLTEpKihGLTEpKS8oKGstMSkqKEYtMSkqKG4qaykpDQojMi1zaWRlcyBGIHRlc3QNCnAyc2lkZXM9MiooMS0ocGYoZixkZjEsZGYyLEYpKSkNCiMxLXNpZGUgRiB0ZXN0DQpwMXNpZGU9KDEtKHBmKGYsZGYxLGRmMixGKSkpDQoNCnJiaW5kKHNzbSxzc3Isc3N0LG1zbSxtc3IsZixkZjEsZGYyLHAxc2lkZSxwMnNpZGVzLGV0YTIsb21lZ2EyLHIsUjIpJT4lcm91bmQoLiwzKSU+JWFzLm1hdHJpeCgpJT4lLlssMV0NCg0KDQoNCmBgYA0KDQpOaMawIHbhuq15LCDEkcOieSBsw6AgbeG7mXQgZ2nhuqNpIHBow6FwICJsYWkiIGdp4buvYSBo4buTaSBxdXkgQmF5ZXMgdsOgIG51bGwgaHlwb3RoZXNpcyB0ZXN0aW5nLiBUaOG6rW0gY2jDrSB0YSBjw7MgY+G6oyBwIHZhbHVlcy4NCg0KS+G6v3QgcXXhuqMgdHLDqm4gxJHDonkgY8OzIHRo4buDIHhlbSBuaMawIHTGsMahbmcgxJHGsMahbmcgduG7m2kga+G6v3QgcXXhuqMgY+G7p2EgcXV5IHRyw6xuaCBBTk9WQSB0aGVvIHRyxrDhu51uZyBwaMOhaSBGcmVxdWVudGlzdC4gR2nDoSB0cuG7iyBGIGfhuqduIG5oxrAgbmhhdSAoNjAgc28gduG7m2kgNTguOCksIHBfdmFsdWUgPDAuMDAxDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpgYGANCg0KIyBQb3N0LWhvYyB0ZXN0IHRoZW8gQkFZRVMNCg0KUXV5IHRyw6xuaCBuw6B5IHRo4buxYyBz4buxIHRoYXkgdGjhur8gcG9zdC1ob2MgdGVzdCB0aGVvIHBow6FpIGZyZXF1ZW50aXN0IGLhurFuZyBwaMawxqFuZyBwaMOhcCBCYXllcy4gTuG7mWkgZHVuZyBj4bunYSBwb3N0IGhvYyB0ZXN0IHRoZW8gQmF5ZXMgcuG6pXQgxJHGoW4gZ2nhuqNuOiBUYSBz4bq9IHThuqFvIHJhIDE1IGNodeG7l2kgTUNNQyBjaOG7qWEgcGjDom4gcGjhu5FpIGjhuq11IMSR4buLbmggY+G7p2Eg4oCca2jDoWMgYmnhu4d0IHRydW5nIGLDrG5o4oCdIGdp4buvYSAyIHBow6JuIG5ow7NtIMSRxrDhu6NjIGLhuq90IGPhurdwIHR14bqnbiB04buxIChUYSBjw7MgNiAgcGjDom4gbmjDs20sIG7Dqm4gY8OzIDE1IGPhurdwIHNvIHPDoW5oICkuIFN1eSBkaeG7hW4gQmF5ZXMgc+G6vSDEkcaw4bujYyB0aOG7sWMgaGnhu4duIHRy4buxYyB0aeG6v3AgdHLDqm4gbeG7l2kgY2h14buXaSBNQ01DLCBraMO0bmcgY+G6p24gZMO5bmcgcF92YWx1ZSBoYXkgaGnhu4d1IGNo4buJbmguDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcG9zdGhvY2RmPWRhdGEuZnJhbWUoDQogIEQxRDI9cmVwKE5BLDQwMDApLA0KICBEMUQzPXJlcChOQSw0MDAwKSwNCiAgRDFEND1yZXAoTkEsNDAwMCksDQogIEQxRDU9cmVwKE5BLDQwMDApLA0KICBEMUQ2PXJlcChOQSw0MDAwKSwNCiAgRDJEMz1yZXAoTkEsNDAwMCksDQogIEQyRDQ9cmVwKE5BLDQwMDApLA0KICBEMkQ1PXJlcChOQSw0MDAwKSwNCiAgRDJENj1yZXAoTkEsNDAwMCksDQogIEQzRDQ9cmVwKE5BLDQwMDApLA0KICBEM0Q1PXJlcChOQSw0MDAwKSwNCiAgRDNENj1yZXAoTkEsNDAwMCksDQogIEQ0RDU9cmVwKE5BLDQwMDApLA0KICBENEQ2PXJlcChOQSw0MDAwKSwNCiAgRDVENj1yZXAoTkEsNDAwMCkNCikNCg0KZm9yIChpIGluIDE6NDAwMCkgew0KICBwb3N0aG9jZGYkRDFEMltpXT1iZXRhZGYkRDJbaV0tYmV0YWRmJEQxW2ldDQogIHBvc3Rob2NkZiREMUQzW2ldPWJldGFkZiREM1tpXS1iZXRhZGYkRDFbaV0NCiAgcG9zdGhvY2RmJEQxRDRbaV09YmV0YWRmJEQ0W2ldLWJldGFkZiREMVtpXQ0KICBwb3N0aG9jZGYkRDFENVtpXT1iZXRhZGYkRDVbaV0tYmV0YWRmJEQxW2ldDQogIHBvc3Rob2NkZiREMUQ2W2ldPWJldGFkZiRENltpXS1iZXRhZGYkRDFbaV0NCiAgcG9zdGhvY2RmJEQyRDNbaV09YmV0YWRmJEQzW2ldLWJldGFkZiREMltpXQ0KICBwb3N0aG9jZGYkRDJENFtpXT1iZXRhZGYkRDRbaV0tYmV0YWRmJEQyW2ldDQogIHBvc3Rob2NkZiREMkQ1W2ldPWJldGFkZiRENVtpXS1iZXRhZGYkRDJbaV0NCiAgcG9zdGhvY2RmJEQyRDZbaV09YmV0YWRmJEQ2W2ldLWJldGFkZiREMltpXQ0KICBwb3N0aG9jZGYkRDNENFtpXT1iZXRhZGYkRDRbaV0tYmV0YWRmJEQzW2ldDQogIHBvc3Rob2NkZiREM0Q1W2ldPWJldGFkZiRENVtpXS1iZXRhZGYkRDNbaV0NCiAgcG9zdGhvY2RmJEQzRDZbaV09YmV0YWRmJEQ2W2ldLWJldGFkZiREM1tpXQ0KICBwb3N0aG9jZGYkRDRENVtpXT1iZXRhZGYkRDVbaV0tYmV0YWRmJEQ0W2ldDQogIHBvc3Rob2NkZiRENEQ2W2ldPWJldGFkZiRENltpXS1iZXRhZGYkRDRbaV0NCiAgcG9zdGhvY2RmJEQ1RDZbaV09YmV0YWRmJEQ2W2ldLWJldGFkZiRENVtpXQ0KfQ0KDQpwb3N0aG9jZGYkcHNldWRvR3JvdXA9ZmFjdG9yKHJlcChjKDE6MjApLGU9bnJvdyhwb3N0aG9jZGYpLzIwKSkNCg0KYGBgDQoNClN1eSBkaeG7hW4gQmF5ZXMgxJHGsOG7o2MgdGjhu7FjIGhp4buHbiBi4bqxbmcgY+G6oyAyIHBoxrDGoW5nIHBow6FwIDogUk9QRSAoS3J1c2Noa2UpIHbDoCBCYXllcyBGYWN0b3IuDQoNClRyxrDhu5tjIGjhur90LCB0YSBow6N5IHhlbSBxdWEgcGjDom4gdMOtY2ggUk9QRSBwaMOibiBwaOG7kWkgaOG6rXUgxJHhu4tuaCBj4bunYSA2IGtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCA6DQoNCk5o4bqvYyBs4bqhaSwgUk9QRSBsw6AgbeG7mXQga2hv4bqjbmcgdsO0IG5naMSpYSB0aOG7sWMgZOG7pW5nLCB0aMOtIGThu6UgdGEgZ2nhuqMgxJHhu4tuZyBy4bqxbmcgbuG6v3Uga2jDoWMgYmnhu4d0IHRydW5nIGLDrG5oIG7hurFtIHRyb25nIGtob+G6o25nICsvLSA1IHhlbSBuaMawIHbDtCBuZ2jEqWEuDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KIyBCYXllcyBJbmZlcmVuY2UNCg0KSERJRj0gZnVuY3Rpb24oIHNhbXBsZVZlYyxjcmVkTWFzcz0wLjk3NSApIHsNCiAgc29ydGVkUHRzID0gc29ydCggc2FtcGxlVmVjICkNCiAgY2lJZHhJbmMgPSBjZWlsaW5nKCBjcmVkTWFzcyAqIGxlbmd0aCggc29ydGVkUHRzICkgKQ0KICBuQ0lzID0gbGVuZ3RoKCBzb3J0ZWRQdHMgKSAtIGNpSWR4SW5jDQogIGNpV2lkdGggPSByZXAoIDAgLCBuQ0lzICkNCiAgZm9yICggaSBpbiAxOm5DSXMgKSB7DQogICAgY2lXaWR0aFsgaSBdID0gc29ydGVkUHRzWyBpICsgY2lJZHhJbmMgXSAtIHNvcnRlZFB0c1sgaSBdDQogIH0NCiAgSERJbWluID0gc29ydGVkUHRzWyB3aGljaC5taW4oIGNpV2lkdGggKSBdDQogIEhESW1heCA9IHNvcnRlZFB0c1sgd2hpY2gubWluKCBjaVdpZHRoICkgKyBjaUlkeEluYyBdDQogIEhESWxpbSA9IGMoIEhESW1pbiAsIEhESW1heCApDQogIHJldHVybiggSERJbGltICkNCn0NCg0KU1VNSz1mdW5jdGlvbihwYXJhbVNhbXBsZVZlYyxjb21wVmFsPU5VTEwgLCBST1BFPU5VTEwgLCBjcmVkTWFzcz0wLjk3NSkgew0KICBtZWFuUGFyYW0gPSBtZWFuKCBwYXJhbVNhbXBsZVZlYyApDQogIG1lZGlhblBhcmFtID0gbWVkaWFuKCBwYXJhbVNhbXBsZVZlYyApDQogIGRyZXMgPSBkZW5zaXR5KCBwYXJhbVNhbXBsZVZlYyApDQogIG1vZGVQYXJhbSA9IGRyZXMkeFt3aGljaC5tYXgoZHJlcyR5KV0NCiAgaGRpTGltID0gSERJRiggcGFyYW1TYW1wbGVWZWMgLCBjcmVkTWFzcz1jcmVkTWFzcyApDQogIGlmICggIWlzLm51bGwoY29tcFZhbCkgKSB7DQogICAgcGNndENvbXBWYWwgPSAoIDEwMCAqIHN1bSggcGFyYW1TYW1wbGVWZWMgPiBjb21wVmFsICkgDQogICAgICAgICAgICAgICAgICAgIC8gbGVuZ3RoKCBwYXJhbVNhbXBsZVZlYyApICkNCiAgfSBlbHNlIHsNCiAgICBjb21wVmFsPU5BDQogICAgcGNndENvbXBWYWw9TkENCiAgfQ0KICBpZiAoICFpcy5udWxsKFJPUEUpICkgew0KICAgIHBjbHRSb3BlID0gKCAxMDAgKiBzdW0oIHBhcmFtU2FtcGxlVmVjIDwgUk9QRVsxXSApIA0KICAgICAgICAgICAgICAgICAvIGxlbmd0aCggcGFyYW1TYW1wbGVWZWMgKSApDQogICAgcGNndFJvcGUgPSAoIDEwMCAqIHN1bSggcGFyYW1TYW1wbGVWZWMgPiBST1BFWzJdICkgDQogICAgICAgICAgICAgICAgIC8gbGVuZ3RoKCBwYXJhbVNhbXBsZVZlYyApICkNCiAgICBwY2luUm9wZSA9IDEwMC0ocGNsdFJvcGUrcGNndFJvcGUpDQogIH0gZWxzZSB7IA0KICAgIFJPUEUgPSBjKE5BLE5BKQ0KICAgIHBjbHRSb3BlPU5BIA0KICAgIHBjZ3RSb3BlPU5BIA0KICAgIHBjaW5Sb3BlPU5BIA0KICB9ICANCiAgcmV0dXJuKCBjKCBNZWFuPW1lYW5QYXJhbSAsIE1lZGlhbj1tZWRpYW5QYXJhbSAsIE1vZGU9bW9kZVBhcmFtICwgDQogICAgICAgICAgICAgSERJbGV2ZWw9Y3JlZE1hc3MgLCBMTD1oZGlMaW1bMV0gLCBVTD1oZGlMaW1bMl0gLCANCiAgICAgICAgICAgICBDb21wVmFsPWNvbXBWYWwgLCBQY250R3RDb21wVmFsPXBjZ3RDb21wVmFsICwgDQogICAgICAgICAgICAgUk9QRWxvdz1ST1BFWzFdICwgUk9QRWhpZ2g9Uk9QRVsyXSAsDQogICAgICAgICAgICAgUGNudEx0Uk9QRT1wY2x0Um9wZSAsIFBjbnRJblJPUEU9cGNpblJvcGUgLCBQY250R3RST1BFPXBjZ3RSb3BlICkgKQ0KfQ0KDQpzdW1tYXJ5S3J1c2Noa2U9ZnVuY3Rpb24oTUNNQyxjb21wVmFsPU5VTEwsIHJvcGU9TlVMTCxjcmVkTWFzcz1OVUxMKXsNCiAgc3VtbWFyeUluZm8gPSBOVUxMDQogIHN1bW1hcnlJbmZvID0gY2JpbmQoc3VtbWFyeUluZm8sICJFc3RpbWF0ZWQiPSBTVU1LKE1DTUMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBWYWw9Y29tcFZhbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk9QRT1yb3BlLGNyZWRNYXNzPWNyZWRNYXNzKSkNCiAgcmV0dXJuKHN1bW1hcnlJbmZvKQ0KfQ0KDQpwb3N0aG9jZGY9cG9zdGhvY2RmJT4lZ2F0aGVyKEQxRDI6RDVENixrZXk9IlBhaXJzIix2YWx1ZT0iRGlmZmVyZW5jZSIpDQoNCnBvc3Rob2NkZiU+JQ0KICBzcGxpdCguJFBhaXJzKSU+JQ0KICBtYXAofnN1bW1hcnlLcnVzY2hrZShNQ01DID0gLiREaWZmZXJlbmNlLA0KICAgICAgICAgICAgICAgICAgICAgICBjb21wVmFsPTAuMCwNCiAgICAgICAgICAgICAgICAgICAgICAgcm9wZT1jKC01LDUpLA0KICAgICAgICAgICAgICAgICAgICAgICBjcmVkTWFzcz0wLjk3NSkNCiAgKSU+JWFzLmRhdGEuZnJhbWUoKS0+a3J1c2Noa2UNCg0KY29sbmFtZXMoa3J1c2Noa2UpPXVuaXF1ZShwb3N0aG9jZGYkUGFpcnMpDQoNCmBgYA0KDQpgYGB7cn0NCmtydXNjaGtlJT4ldCgpJT4lcm91bmQoLiwyKQ0KYGBgDQoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCnBvc3Rob2NkZiU+JWdncGxvdCgpKw0KICBnZW9tX2pveShhZXMoeT1yZW9yZGVyKFBhaXJzLERpZmZlcmVuY2UpLA0KICAgICAgICAgICAgICAgeD1EaWZmZXJlbmNlLA0KICAgICAgICAgICAgICAgZmlsbD1yZW9yZGVyKFBhaXJzLERpZmZlcmVuY2UpLA0KICAgICAgICAgICAgICAgY29sPXBzZXVkb0dyb3VwKSwNCiAgICAgICAgICAgYWxwaGE9MC4wNSwNCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLA0KICAgICAgICAgICBzY2FsZT0xKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoLTUsMC4wLDUpLGNvbD0icmVkNCIsbGluZXR5cGU9MikrDQogIHlsYWIoIlBhaXJzIikrDQogIHRoZW1lX2J3KCkrZ2d0aXRsZSgiQmF5ZXNpYW4gUGFpcndpc2UgY29tcGFyaXNvbiIpKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUHVSZCIpKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VCxvcHRpb249IkMiLGRpcmVjdGlvbiA9IC0xKQ0KDQpgYGANCg0KS+G6v3QgcXXhuqM6IE5nb+G6oWkgdHLhu6sgY+G6t3AgRDFEMywgRDJEMyxEMUQyIHbDoCBENUQ2LCB0xrDGoW5nIOG7qW5nIHbhu5tpICB2aeG7h2MgY2h1eeG7g24gdOG7qyBsaeG7gXUgdGjhuqVwIG5o4bqldCBzYW5nIGxp4buBdSBr4bq/IGPhuq1uIGNobyDEkeG6v24gRDQsIHbDoCB04burIGxp4buBdSBjYW8gbmjhuqV0IEQ1IHNhbmcgMSBsaeG7gXUgY2FvIGjGoW4gbMOgIEQ2IGtow7RuZyBjaG8gdGjhuqV5IGtow6FjIGJp4buHdCByw7UgbsOpdDsgdGjDrCAyIGPhurdwIEQzRDQgdsOgIEQ0RDUgxJHhu4F1IGNobyB0aOG6pXkgbeG7mXQgc+G7sSB0xrDGoW5nIHBo4bqjbiBraMOhIHLDtSwgdsOsIG3huq10IMSR4buZIHBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIGtow6FjIGJp4buHdCB0cnVuZyBiw6xuaCBu4bqxbSB0cm9uZyBST1BFICBsw6AgMC41NSUgKGPhurdwIEQzRDQpLHbDoCAxLjU4JSAoRDRENSksIHRyb25nIGtoaSA5OC40JSB2w6AgOTkuNSUgbeG6rXQgxJHhu5kga2jDoWMgYmnhu4d0IHRydW5nIGLDrG5oIG7hurFtIG5nb8OgaSBST1BFIHbDoCA+IDUuDQoNCkvhur90IHF14bqjIG7DoHkgaG/DoG4gdG/DoG4gdMawxqFuZyDEkeG7k25nIHbhu5tpIGvhur90IHF14bqjIGPhu6dhIENvbnRyYXN0IGFuYWx5c2lzLiBDaMO6bmcgdMO0aSBraMO0bmcgbXXhu5FuIMOhcCDEkeG6t3Qgw70ga2nhur9uIHJpw6puZyBuw6BvIGPhuqMgduG7gSBr4bq/dCBxdeG6oyBuw6B5LiBDw6FjIGLhuqFuIGPDsyB0b8OgbiBxdXnhu4FuIFRpbiBob+G6t2Mga2jDtG5nIFRpbiB24buBIGtow6FjIGJp4buHdCBjw7Mgw70gbmdoxKlhIGNobyB2aeG7h2MgY2h1eeG7g24gbGnhu4F1IHThu6sgRDMgc2FuZyBENCB2w6AgRDUuDQoNCkPDsyBt4buZdCBjw6FjaCBraMOhYyBu4bq/dSBi4bqhbiBtdeG7kW4gcGjDom4gxJHhu4tuaCBUcuG6r25nL8SQZW46IMSRw7MgbMOgIGTDuW5nIEJheWVzIEZhY3RvciwgduG7m2kgbmfGsOG7oW5nIHNvIHPDoW5oIGzDoCB6ZXJvIChraMO0bmcgY8OzIGtow6FjIGJp4buHdCksIHThu4kgdHLhu41uZyBjaOG7qW5nIGPhu6kg4bunbmcgaOG7mSBjaG8gZ2nhuqMgdGh1eeG6v3QgSDEgKGtow6FjIGJp4buHdCA8IDApOg0KDQpEbyBjw7MgcXXDoSBuaGnhu4F1IGPhurdwIHNvIHPDoW5oICgxNSksIHRhIGPDsyB0aOG7gyBraOG6o28gc8OhdCB0cuG7sWMgcXVhbiDEkeG7mSBs4bubbiBj4bunYSBCRiBjaG8gMTUgbmfGsOG7oW5nIHNvIHPDoW5oIHThu6sgLTEwIMSR4bq/biArMjAgdHJvbmcgMSBoZWF0bWFwIG5oxrAgc2F1OiANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCiMgQmF5ZXMgRmFjdG9ycw0KDQpsaWJyYXJ5KGJybXMpDQoNCnRocmVzPWMoLTEwOjIwKQ0KDQp0aHJlc2hvbGQ9cmVwKE5BLDE1Kmxlbmd0aCh0aHJlcykpDQpCYXllc0ZhY3Rvcj1yZXAoTkEsMTUqbGVuZ3RoKHRocmVzKSkNClBhaXI9cmVwKE5BLDE1Kmxlbmd0aCh0aHJlcykpDQoNCnBhaXJsZXY9dW5pcXVlKHBvc3Rob2NkZiRQYWlycykNCg0Kbj0wDQoNCmZvcihpIGluICgxOjE1KSl7DQogIGZvciAoaiBpbiAoMTpsZW5ndGgodGhyZXMpKSl7DQogICAgdGVtcGRmPXN1YnNldChwb3N0aG9jZGYsUGFpcnM9PXBhaXJsZXZbaV0pDQogICAgUGFpcltuK2pdPXBhaXJsZXZbaV0NCiAgICB0aHI9dGhyZXNbal0NCiAgICB0aHJlc2hvbGRbbitqXT10aHINCiAgICBoeXA9cGFzdGUoIkRpZmZlcmVuY2U+Iix0aHIsc2VwPSIiKQ0KICAgIGJmPWh5cG90aGVzaXModGVtcGRmLGh5cCxhbHBoYT0wLjA1KQ0KICAgIEJheWVzRmFjdG9yW24ral09YmYkaHlwb3RoZXNpcyRFdmlkLlJhdGlvDQogIH0NCiAgbj1uK2xlbmd0aCh0aHJlcykNCn0NCg0KdGVtcGJmZGY9Y2JpbmQoUGFpcix0aHJlc2hvbGQsQmF5ZXNGYWN0b3IpJT4lYXNfdGliYmxlKCkNCnRlbXBiZmRmJHRocmVzaG9sZD1hcy5udW1lcmljKHRlbXBiZmRmJHRocmVzaG9sZCkNCnRlbXBiZmRmJEJheWVzRmFjdG9yPWFzLm51bWVyaWModGVtcGJmZGYkQmF5ZXNGYWN0b3IpDQoNCmZvcihpIGluICgxOm5yb3codGVtcGJmZGYpKSl7DQogIHRlbXBiZmRmJEJheWVzRmFjdG9yW2ldPC1pZmVsc2UodGVtcGJmZGYkQmF5ZXNGYWN0b3JbaV0+OTk5OSw5OTk5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBiZmRmJEJheWVzRmFjdG9yW2ldKQ0KfQ0KDQoNCmBgYA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnRlbXBiZmRmJT4lZ2dwbG90KGFlcyh4PXRocmVzaG9sZCx5PXJlb3JkZXIoUGFpcixCYXllc0ZhY3RvciksZmlsbD1CYXllc0ZhY3Rvcixjb2w9QmF5ZXNGYWN0b3IpKSsNCiAgZ2VvbV90aWxlKGFscGhhPTAuNykrDQogIHRoZW1lX2J3KCkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDAsNSksY29sPSJibHVlMyIpKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlPUYsb3B0aW9uPSJEIixkaXJlY3Rpb249LTEpKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RixvcHRpb249IkQiLGRpcmVjdGlvbj0tMSkNCmBgYA0KDQpL4bq/dCBxdeG6oyBjaG8gdGjhuqV5IEJGIHLhuqV0IGNhbyBjaG8gbmfGsOG7oW5nIHNvIHPDoW5oID0wIHbDoCBjw6FjIGPhurdwIEQ0RDUgKEJGIGtob+G6o25nIDMwMDApIHbDoCBEM0Q0IChCRiA+IDkwMDApDQoNCiMgRGnhu4VuIMSR4bqhdCB2xINuIGLhuqNuIGtob2EgaOG7jWMNCg0KKlBoxrDGoW5nIHBow6FwIHRo4buRbmcga8OqKg0KDQpIaeG7h3Ug4bupbmcgY+G7p2EgbGnhu4F1IHRodeG7kWMgcGhlbnlsYmlndWFuaWRlIHRyw6puIHPhu7EgdGhheSDEkeG7lWkgY+G7p2EgaHV54bq/dCDDoXAgdGjhu48gxJHGsOG7o2Mga2jhuqNvIHPDoXQgYuG6sW5nIG3hu5l0IG3DtCBow6xuaCBCYXllcyBjaG8gcGjDqXAgxJFvIGzhurdwIGzhuqFpIHbhu5tpIGxp4buBdSB0aHXhu5FjIChrPTYpIGzDoCBoaeG7h3Ug4bupbmcgY2jDrW5oLCBzYWkgYmnhu4d0IGPGoSBi4bqjbiDhu58gdOG7q25nIGPDoSB0aOG7gyAoaT01KSBsw6AgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4uIE3DtCBow6xuaCBuw6B5IGNobyByYSBwaMOibiBwaOG7kWkgaOG6rXUgbmdoaeG7h20gY+G7p2EgdHJ1bmcgYsOsbmgga2jDoWMgYmnhu4d0IGPhu6dhIGh1eeG6v3Qgw6FwIGtoaSBjaHV54buDbiB04burIGxp4buBdSB0aOG6pXAgc2FuZyBsaeG7gXUgY2FvIGjGoW4uIFN1eSBkaeG7hW4gdGjhu5FuZyBrw6ogZOG7sWEgdsOgbyBIREkgKGtob+G6o25nIG3huq10IMSR4buZIGNhbyBuaOG6pXQpLCBST1BFICh0aGVvIEtydXNjaGtlKSB2w6AgdOG7iSB0cuG7jW5nIGNo4bupbmcgY+G7qSAoQmF5ZXMgZmFjdG9yKS4NCg0KKkvhur90IHF14bqjKg0KDQpNw7QgaMOsbmggQmF5ZXMgY2hvIHRo4bqleSBsaeG7gXUgdGh14buRYyBnw6J5IHJhIGhp4buHdSDhu6luZyDDvSBuZ2jEqWEgbMOgbSB0xINuZyBodXnhur90IMOhcCAoRig1LDIwKT01OC43OCwgcF92YWx1ZTwwLjAwMDEgOyBvbWVnYXNxdWFyZWQgPSAwLjg5NykuIFBow6JuIHBo4buRaSBo4bqtdSDEkeG7i25oIGPhu6dhIGdpw6EgdHLhu4sgaHV54bq/dCDDoXAgdHJ1bmcgYsOsbmggdsOgIHRydW5nIGLDrG5oIGtow6FjIGJp4buHdCBnaeG7r2EgY8OhYyBsaeG7gXUgdGh14buRYyDEkcaw4bujYyB0csOsbmggYsOgeSB0cm9uZyBow6xuaCAuLi4gUGjDom4gdMOtY2ggUk9QRSAoYuG6o25n4oCmKSB2w6AgQkYgZmFjdG9yIChIw6xuaOKApikgY2hvIHRo4bqleSBraHV5bmggaMaw4bubbmcgbmjGsCBzYXUgOiBIdXnhur90IMOhcCBraMO0bmcgdGhheSDEkeG7lWkgw70gbmdoxKlhIHRyb25nIHBo4bqhbSB2aSBsaeG7gXUgRDEgxJHhur9uIEQzLCBuaMawbmcgdMSDbmcgxJHhu5l0IG5n4buZdCBraGkgY2h1eeG7g24gdOG7qyBEMyBzYW5nIEQ0LCBzYXUgxJHDsyB0xINuZyBuaOG6uSBraGkgbGnhu4F1IHTEg25nIHThu6sgRDQgc2FuZyBENSB2w6AgZ2nhuqNtIGThuqduIGtoaSBjaHV54buDbiB04burIEQ1IHF1YSBENi4NCg0KIyBCw6BuIGx14bqtbiB2w6AgVOG7lW5nIGvhur90DQoNClR1eSB0aMOtIGThu6UgbWluaCBo4buNYSB0cm9uZyBiw6BpIGNoxrBhIGhvw6BuIGjhuqNvIMSR4buRaSB24bubaSB0aMOtIG5naGnhu4dtIEFOT1ZBIHJlcGVhdGVkIG1lYXN1cmVkIHbDrCBraMO0bmcgcGjDom4gYmnhu4d0IHLDtSBiZXR3ZWVuLCB3aXRoaW4gZWZmZWN0IHbDoCB54bq/dSB04buRIHRo4budaSBnaWFuOyBuaMawbmcgxJHDonkgbMOgIG3hu5l0IHRyxrDhu51uZyBo4bujcCB0aMO6IHbhu4ssIGNobyB0aOG6pXkgxrB1IHRo4bq/IGPhu6dhIHBoxrDGoW5nIHBow6FwIEJheWVzIHNvIHbhu5tpIEFOT1ZBIGPhu5UgxJFp4buDbjsgdsOsIHRo4buxYyByYSwgdHLDqm4gZOG7ryBsaeG7h3UgbsOgeSB0YSBraMO0bmcgdGjhu4MgbMOgbSBBTk9WQSDEkcaw4bujYyAoY+G7oSBt4bqrdSBxdcOhIHRo4bqlcDogbj01IGNobyBt4buXaSBwaMOibiBuaMOzbSwgdmkgcGjhuqFtIGdp4bqjIMSR4buLbmggcGjDom4gcGjhu5FpIGNodeG6qW4pOyBuaMawbmcga2hpIGTDuW5nIEJheWVzIGtow7RuZyBj4bqnbmLhuq1uIHTDom0gxJHhur9uIGNodXnhu4duIG7DoHkuIFZp4buHYyBxdXkgxJHhu4tuaCBsaWtlbGlob29kIHbDoCBwcmlvciB0cm9uZyBtw7QgaMOsbmggQmF5ZXMgY8WpbmcgbMOgIGdp4bqjIMSR4buLbmgsIG5oxrBuZyBuw7Mga2jDtG5nIGLhuq90IGJ14buZYywga2jDtG5nIGPhu5EgxJHhu4tuaC4gTuG6v3UgbXXhu5FuLCB0YSBk4buFIGTDoG5nIGTDuW5nIG3hu5l0IGxpa2VsaWhvb2QgdGhlbyBwaMOibiBwaOG7kWkga2jDoWMgY2hvIFkgKHRow60gZOG7pSBHYW1tYSBoYXkgbG9nTm9ybWFsLi4uKSwgdGEgY8WpbmcgY8OzIHRo4buDIHRoYXkgxJHhu5VpIHByaW9yIHTDuXkgdGjDrWNoLiBMw70gZG8gdGEgY2jhu41uIHBow6JuIHBo4buRaSBub3JtYWwgY2jhu4kgxJHGoW4gZ2nhuqNuIHbDrCDEkcOieSBsw6AgZ2nhuqMgxJHhu4tuaCBo4bujcCBsw70gbmjhuqV0IGNobyBiaeG6v24gbGnDqm4gdOG7pWMga2hpIHRhIGtow7RuZyBjw7MgbeG7mXQgdGjDtG5nIHRpbiBuw6BvIGtow6FjLiANCg0KxJDDonkgbMOgIGzhuqduIMSR4bqndSB0acOqbiBjw6FjIGLhuqFuIGzDoG0gIE1peGVkIG1vZGVsIHRyb25nIFNUQU4sIHbhuqVuIMSR4buBIGzDoDogS2hpIGTDuW5nIHBoxrDGoW5nIHBow6FwIEJheWVzLCBjw6FjIGLhuqFuIGtow7RuZyBj4bqnbiBxdWFuIHTDom0gxJHhur9uIGPGoSBjaOG6vyByacOqbmcgY+G7p2EgYWxnb3JpdGhtIGNobyB04burbmcgbG/huqFpIG3DtCBow6xuaCAodmnhu4djIMSRw7MgxJHDoyBjw7MgTUNNQyBzYW1wbGVyIGxvKSwgbcOgIGNo4buJIGPhuqduIHF1YW4gdMOibSDEkeG6v24gdmnhu4djIGdp4bqjaSBwaOG6q3UgY+G6pXUgdHLDumMgbcO0IGjDrG5oIHbDoCBtw7QgdOG6oyBj4bqldSB0csO6YyBuw6B5IHRyb25nIFNUQU4uIENo4buJIHbhu5tpIDIgcGFyYW1ldGVycywgdGEgY8OzIHRo4buDIHRow6ptIFJhbmRvbSBlZmZlY3QgdsOgbyBtb2RlbC4gVHJvbmcgdGjhu51pIGdpYW4gdOG7m2ksIG5ow7NtIEJBViBz4bq9IGdp4bubaSB0aGnhu4d1IGNobyBjw6FjIGLhuqFuIG5o4buvbmcgbcO0IGjDrG5oIGPDsm4gcGjhu6ljIHThuqFwIGjGoW4sbmjGsCB0aGF5IMSR4buVaSBmYW1pbHkgZGlzdHJpYnV0aW9uIGNobyBsaWtlbGlob29kIHRow6BuaCBQb2lzc29uIGNo4bqzbmcgaOG6oW4uIA0KDQpDw6FjIHRow7RuZyDEkWnhu4dwIHF1YW4gdHLhu41uZyDEkcaw4bujYyBjaHV54buDbiB04bqjaSB0cm9uZyBiw6BpOg0KDQoxKQlBTk9WQSBjaG8gdGjDrSBuZ2hp4buHbSBs4bq3cCBs4bqhaSBsw6AgbeG7mXQgdHLGsOG7nW5nIGjhu6NwIHThu5VuZyBxdcOhdCBjaG8gcGFpcmVkIHNhbXBsZSB0IHRlc3QNCg0KMikgQ+G6p24gcGjDom4gYmnhu4d0IGdp4buvYSBSZXBlYXRlZCBtZWFzdXJlIHbDoCBMb25naXR1ZGluYWwgc3R1ZHksIMSR4buDIMOhcCBk4bulbmcgcGjDuSBo4bujcC4NCg0KMykgTsOqbiB0aeG6v3AgY+G6rW4gUmVwZWF0ZWQgbWVhc3VyZSBBTk9WQSBi4bqxbmcgTWl4ZWQgbW9kZWwgdGhheSBjaG8gZ2nhuqNpIHBow6FwIMSRYSBiaeG6v24gdsOgIHTDrW5oIHRo4bunIGPDtG5nLiBNaXhlZCBtb2RlbCBsaW5oIGhv4bqhdCBoxqFuIG5oaeG7gXUuDQoNCjQpCVBoxrDGoW5nIHBow6FwIHN1eSBkaeG7hW4gQmF5ZXMgbGluaCBob+G6oXQgdsOgIGPDsyBuaGnhu4F1IMawdSB0aOG6vyBoxqFuIHNvIHbhu5tpIE51bGwgaHlwb3RoZXNpcyB0ZXN0aW5nIHbDoCBQX3ZhbHVlDQoNCk5ow7NtIEJBViB4aW4gY2jDom4gdGjDoG5oIGPhuqNtIMahbiBz4buxIOG7p25nIGjhu5kgY+G7p2EgY8OhYyBi4bqhbi4gQ2jDum5nIHTDtGkgc+G6vSBn4bq3cCBs4bqhaSBjw6FjIGLhuqFuIHRyb25nIG3hu5l0IGLDoGkga2jDoWMu