1 Giới thiệu

Như câu chuyện ngụ ngôn “Thầy bói sờ voi”, nhiều tác giả đã dùng những thí nghiệm mô phỏng để cảnh báo về nguy cơ sai lầm và nhược điểm của các phương pháp thống kê quy ước, bao gồm Mean, Sd, Pearson’s r, Boxplot..

Thí dụ:

Nghịch lý Simpson E.H (1951) là một can thiệp nhắm vào hệ số tương quan r của Pearson; theo đó : Trong dữ liệu được tái cấu trúc như hình B: Rõ ràng là Y tương quan nghịch với X, nhưng kết quả thống kê (Hình C) cho ra giá trị r>0 và tương đương với một dữ liệu có tương quan thuận trong hình A.

Năm 1973, ta biết đến bộ tứ Anscombe với 4 dữ liệu với cấu trúc hoàn toàn khác nhau nhưng đều cho ra cùng kết quả Trung bình, cùng độ lệch chuẩn và cùng hệ số tương quan. Dĩ nhiên ta có thể phủ nhận ý tưởng của Anscombe vì cho rằng nó quá cực đoan. Nhưng vào năm 2016, Justin Matekja và George Fitzmaurice (https://www.autodeskresearch.com/publications/samestats) đã sử dụng một thuật toán mô phỏng để hiện thực hóa bộ tứ Anscombe theo hướng ngẫu nhiên, phi cấu trúc. Kết quả cho thấy cả 8 bộ dữ liệu này đều có chung kết quả Mean, Sd, Pearson’s r. Kết quả này cho thấy là nguy cơ nhầm lẫn hoàn toàn có thể xảy ra trên thực tế. Một cách giải thích cho nghịch lý này, đó là “Regression towards the means” hay hiện tượng Hồi quy về trung bình.

2 12 biến thể Datasaurus

Cũng trong năm 2016, Cairo A. còn đi xa hơn nữa khi giới thiệu không chỉ 4, mà đến 12 biến thể dữ liệu. Thí nghiệm nổi tiếng này có tên là Datasaurus Dozen. Trong bài này, Nhi sẽ cùng các bạn khảo sát bộ dữ liệu này.

Trước hết ta tải file Datasaurus từ đây: https://www.autodeskresearch.com/sites/default/files/The%20Datasaurus%20Dozen.zip

library(tidyverse)

df=read_tsv("DatasaurusDozen.tsv")%>%as_tibble()

3 Các trị số thống kê mô tả

Bộ số liệu có dạng “long format”, gồm 3 biến, biến dataset cho phép phân chia dataframe thành 13 nhóm khác nhau, mỗi bộ dữ liệu nhỏ này có 2 biến liên tục X và Y, với số lượng như nhau (n=142).

Thống kê mô tả với Mean và SD cho thấy cả 13 bộ số liệu đều có Mean X = 54.27 và SD của X = 16.77

df %>%
  group_by(dataset) %>%
  summarise_at("x",funs(Size=length,
                        Mean=mean,
                        SD=sd))%>%knitr::kable()
dataset Size Mean SD
away 142 54.26610 16.76983
bullseye 142 54.26873 16.76924
circle 142 54.26732 16.76001
dino 142 54.26327 16.76514
dots 142 54.26030 16.76774
h_lines 142 54.26144 16.76590
high_lines 142 54.26881 16.76670
slant_down 142 54.26785 16.76676
slant_up 142 54.26588 16.76885
star 142 54.26734 16.76896
v_lines 142 54.26993 16.76996
wide_lines 142 54.26692 16.77000
x_shape 142 54.26015 16.76996

Làm tương tự cho biến Y, ta sẽ thấy kết quả là Mean Y=47.83 và SD Y=26.94, như nhau cho 13 nhóm.

df %>%
  group_by(dataset) %>%
  summarise_at("y",funs(Size=length,
                        Mean=mean,
                        SD=sd))%>%knitr::kable()
dataset Size Mean SD
away 142 47.83472 26.93974
bullseye 142 47.83082 26.93573
circle 142 47.83772 26.93004
dino 142 47.83225 26.93540
dots 142 47.83983 26.93019
h_lines 142 47.83025 26.93988
high_lines 142 47.83545 26.94000
slant_down 142 47.83590 26.93610
slant_up 142 47.83150 26.93861
star 142 47.83955 26.93027
v_lines 142 47.83699 26.93768
wide_lines 142 47.83160 26.93790
x_shape 142 47.83972 26.93000

Phân tích tương quan giữa Y và X cho thấy cả 13 nhóm đều có hệ số tương quan Pearson’s r rất gần nhau, khoảng từ -0.063 đến -0.069

df%>%split(.$dataset)%>%map(~cor(.$x,.$y,method="pearson"))%>%cbind()
##            .          
## away       -0.06412835
## bullseye   -0.06858639
## circle     -0.06834336
## dino       -0.06447185
## dots       -0.06034144
## h_lines    -0.06171484
## high_lines -0.06850422
## slant_down -0.06897974
## slant_up   -0.06860921
## star       -0.0629611 
## v_lines    -0.06944557
## wide_lines -0.06657523
## x_shape    -0.06558334

Xa hơn nữa, ta có thể thấy là 13 mô hình hồi quy Y~X đều có nội dung như nhau:

df%>%split(.$dataset)%>%
  map(~broom::tidy(lm(.$y~.$x)))
## $away
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.4251300 7.6932059  6.9444560 1.308597e-10
## 2         .$x -0.1030184 0.1354896 -0.7603419 4.483288e-01
## 
## $bullseye
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.8094708 7.6903593  6.9970035 9.917561e-11
## 2         .$x -0.1101675 0.1354339 -0.8134407 4.173467e-01
## 
## $circle
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.7970450 7.6925470  6.9933983 1.010830e-10
## 2         .$x -0.1098143 0.1354821 -0.8105447 4.190029e-01
## 
## $dino
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.4529784 7.6933924  6.9479075 1.285021e-10
## 2         .$x -0.1035825 0.1355026 -0.7644316 4.458966e-01
## 
## $dots
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.0983419 7.6924229  6.9026811 1.630167e-10
## 2         .$x -0.0969127 0.1354905 -0.7152729 4.756316e-01
## 
## $h_lines
##          term    estimate std.error  statistic      p.value
## 1 (Intercept) 53.21108724 7.6954597  6.9146080 1.531137e-10
## 2         .$x -0.09916499 0.1355427 -0.7316144 4.656268e-01
## 
## $high_lines
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.8087932 7.6926945  6.9947914 1.003417e-10
## 2         .$x -0.1100695 0.1354766 -0.8124615 4.179063e-01
## 
## $slant_down
##          term   estimate std.error statistic      p.value
## 1 (Intercept) 53.8497077 7.6911834  7.001485 9.685386e-11
## 2         .$x -0.1108172 0.1354522 -0.818128 4.146744e-01
## 
## $slant_up
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.8125956 7.6909632  6.9968604 9.925065e-11
## 2         .$x -0.1102184 0.1354513 -0.8137125 4.171915e-01
## 
## $star
##          term  estimate std.error  statistic      p.value
## 1 (Intercept) 53.326679 7.6915981  6.9331078 1.389166e-10
## 2         .$x -0.101113 0.1354591 -0.7464467 4.566492e-01
## 
## $v_lines
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.8908434 7.6903136  7.0076262 9.375965e-11
## 2         .$x -0.1115508 0.1354299 -0.8236796 4.115226e-01
## 
## $wide_lines
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.6349489 7.6914773  6.9732961 1.124023e-10
## 2         .$x -0.1069408 0.1354572 -0.7894803 4.311664e-01
## 
## $x_shape
##          term   estimate std.error  statistic      p.value
## 1 (Intercept) 53.5542263 7.6888696  6.9651625 1.173303e-10
## 2         .$x -0.1053169 0.1354267 -0.7776668 4.380777e-01

4 Sự thật về Datasaurus

Sự thật về cấu trúc của 13 bộ số liệu này chỉ được phơi bày khi ta mô tả trực quan với biểu đồ tán xạ. Bạn sẽ bất ngờ vì 13 hình ảnh hoàn toàn khác biệt:

df%>%ggplot(aes(x,y))+
  geom_point(aes(col=dataset),size=1)+
  theme_bw(8)+
  coord_equal()+
  facet_wrap(~dataset,ncol=4,scales="free")

Như vậy, chắc các bạn cũng đồng ý rằng những trị số Trung bình và SD hoàn toàn bị hack (đánh lừa), chúng không đủ để mô tả trung thực về đặc tính phân bố của dữ liệu.

Câu hỏi đặt ra đó là: Nếu Mean và SD có nguy cơ sai lầm cao như vậy, liệu ta có thể tránh nhưng ảo giác này bằng những phép thống kê mô tả khác hay không ?

Để trả lời, Nhi sẽ lần lượt thử nghiệm những cách làm khác nhau:

  1. Đầu tiên, ta sử dụng thêm Median, IQR, Skewness, kurtosis cho biến Y

Kết quả cho thấy Median, Skewness, Kurtosis và IQR không bị ảnh hưởng bởi thủ thuật can thiệp trong 13 bộ dữ liệu datasaurus.

library(e1071)

df %>%
  group_by(dataset) %>%
  summarise_at("y",funs(Mean=mean,
                        Median=median,
                        Skewness=skewness,
                        Kurtosis=kurtosis,
                        IQR=IQR))%>%knitr::kable()
dataset Mean Median Skewness Kurtosis IQR
away 47.83472 47.53527 -0.0932474 -1.2817225 47.17726
bullseye 47.83082 47.38294 0.0122276 -1.4540983 46.28812
circle 47.83772 51.02502 0.0048960 -1.8029697 59.43277
dino 47.83225 46.02560 0.2472603 -1.0635521 43.23723
dots 47.83983 51.29929 0.1476910 -1.4088664 65.77445
h_lines 47.83025 50.47353 0.1487355 -1.1603030 39.86956
high_lines 47.83545 32.49920 0.2024742 -1.8022348 53.01918
slant_down 47.83590 46.40131 0.1873369 -0.9492562 40.59857
slant_up 47.83150 45.29224 0.2554495 -1.0933159 46.09960
star 47.83955 50.11055 0.2372490 -1.3589160 43.17445
v_lines 47.83699 47.11362 0.2253746 -1.0479510 43.09251
wide_lines 47.83160 46.27933 0.2484350 -1.0901205 43.22119
x_shape 47.83972 39.87621 0.2277144 -1.4442001 50.13882
  1. Bách phân vị, Min và Max

Tương tự, khảo sát bách phân vị cũng cho phép mô tả trung thực nhất đặc tính phân bố của biến Y

df %>%
  group_by(dataset) %>%
  summarise_at("x",funs(Min=min,Max=max,
                        Q5=quantile(.,probs=0.05),
                        Q25=quantile(.,probs=0.55),
                        Median=quantile(.,probs=0.5),
                        Q75=quantile(.,probs=0.75),
                        Q95=quantile(.,probs=0.95)))%>%knitr::kable()
dataset Min Max Q5 Q25 Median Q75 Q95
away 15.56075 91.63996 30.41458 59.84299 53.34030 69.14660 78.04134
bullseye 19.28820 91.73554 25.18011 55.11872 53.84209 64.79890 88.42245
circle 21.86358 85.66476 26.31463 56.32046 54.02321 64.97267 84.61663
dino 22.30770 98.20510 29.50002 55.26922 53.33330 64.74360 85.03845
dots 25.44353 77.95444 25.98733 51.88621 50.97677 75.19736 77.24589
h_lines 22.00371 98.28812 27.17405 54.89325 53.06968 66.76827 80.39223
high_lines 17.89350 96.08052 27.31751 56.54162 54.16869 63.95267 84.88861
slant_down 18.10947 95.59342 27.74931 55.07874 53.13516 64.46999 82.77544
slant_up 20.20978 95.26053 27.89781 55.57817 54.26135 64.48801 85.79441
star 27.02460 86.43590 28.59477 57.58533 56.53473 68.71149 79.16531
v_lines 30.44965 89.50485 30.47846 50.42004 50.36289 69.50407 89.49400
wide_lines 27.43963 77.91587 30.80572 65.29557 64.55023 67.45367 74.16283
x_shape 31.10687 85.44619 34.81317 50.75597 47.13646 71.85692 81.52456
  1. Bootstrap trung bình

Ta cũng có thể áp dụng một phân tích trung bình kèm theo bootstrap. Việc chọn mẫu ngẫu nhiên có thể khắc phục nguy cơ sai lầm: Giá trị Mean tính được cho y sau 100 lần bootstrap không còn đồng nhất như ban đầu:

library(broom)

set.seed(1234)

bootmean=function(data,n){
  data%>%bootstrap(n)%>%
  do(tidy(lm(.$y~1)))->temp
  mean(temp$estimate)
}

df%>%split(.$dataset)%>%map(~bootmean(.,n=100))%>%cbind()
##            .       
## away       47.64329
## bullseye   47.96658
## circle     48.24137
## dino       47.47619
## dots       47.75413
## h_lines    47.98973
## high_lines 47.85328
## slant_down 48.03726
## slant_up   48.23992
## star       48.07929
## v_lines    47.96137
## wide_lines 47.75999
## x_shape    48.06804
  1. Tương quan phi tham số:

Vì dữ liệu X và Y đều có phân bố bất thường, do đó khi ta sử dụng phương pháp tương quan Spearman’s rho, kết quả sẽ khác hẳn so với Pearson’s r.

df%>%split(.$dataset)%>%map(~cor(.$x,.$y,method="spearman"))%>%cbind()
##            .           
## away       -0.05729991 
## bullseye   -0.07873367 
## circle     -0.0772919  
## dino       -0.06510904 
## dots       -0.1263792  
## h_lines    -0.0519729  
## high_lines -0.002868872
## slant_down -0.06693546 
## slant_up   -0.0860976  
## star       -0.05144481 
## v_lines    -0.05662093 
## wide_lines -0.05223275 
## x_shape    -0.02053475

5 Mô tả trực quan

Kết luận hiển nhiên từ 2D scatter plot của Datasaurus cho thấy việc mô tả trực quan có vai trò rất quan trọng, số liệu thống kê là hư ảo, nhưng biểu đồ luôn nói lên sự thật. Tuy nhiên, Nhi muốn tìm hiểu xa hơn một chút, với câu hỏi: Ngoài 2D plot, khi muốn mô tả đặc tính phân bố 1 chiều (đơn biến), dạng biểu đồ nào là tốt nhất ?

Ta sẽ lần lượt thử các giải pháp sau đây cho biến Y:

  1. Point range (Điểm và error bar)

Kết quả cho thấy Point range plot là dạng biểu dồ đơn giản nhất, nhưng nó hoàn toàn thất bại và bị đánh lừa :

df%>%ggplot(aes(x=dataset,y))+
  stat_summary(aes(col=dataset),
               fun.data = "mean_cl_boot",
               size=1)+theme_bw()+coord_flip()

  1. Boxplot:

Boxplot là dạng biểu đồ rất thông dụng để mô tả đặc tính phân bố cho 1 biến. Tuy nhiên, chính nguyên lý đơn giản (chỉ sử dụng 5 thông tin) trong Boxplot khiến nó có nguy cơ bị ngộ nhận cao. Khi áp dụng cho 13 bộ dữ liệu Datasaurus, khả năng phân biệt của Boxplot rất kém, hơn nữa nó còn gây ảo giác về phân bố chuẩn, liên tục của biến Y trong một số trường hợp như nhóm h_lines, dots, circle, dino.

p1=df%>%ggplot(aes(x=dataset,y=y))+
  geom_boxplot(aes(fill=dataset),alpha=0.5,show.legend = F)+
  coord_flip()+
  theme_bw()

p2=df%>%ggplot(aes(x=dataset,y=y))+
  geom_jitter(aes(col=dataset),size=1,alpha=0.6,show.legend = F)+
  scale_x_discrete(breaks=NULL)+
  coord_flip()+
  theme_bw()

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

  1. Violin plot:

Violin plot có ưu thế cao hơn hẳn so với boxplot, trong trường hợp này nếu sử dụng violin plot là có thể phân biệt dễ dàng đặc tính phân phối của Y giữa 13 bộ số liệu. Nhược điểm duy nhất của violin plot là nó vẫn còn bị đánh lừa bởi cấu trúc đối xứng trong các nhóm hlines và bullseye; kết quả sẽ gây ảo giác về một phân phối uniform trong khi sự thật không phải như vậy.

p1=df%>%ggplot(aes(x=dataset,y))+
  geom_violin(aes(fill=dataset),alpha=0.5,show.legend = F)+
  coord_flip()+
  theme_bw()

p2=df%>%ggplot(aes(x=dataset,y))+
  geom_jitter(aes(col=dataset),size=1,alpha=0.6,show.legend = F)+
  scale_x_discrete(breaks=NULL)+
  coord_flip()+
  theme_bw()

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

  1. Histogram

Histogram là dạng biểu đồ tốt nhất để mô tả đặc tính phân phối đơn biến. Thật vậy, histogram cung cấp hình ảnh trung thực nhất, hơn nữa - có thể tinh chỉnh tùy ý - về mật độ phân bố.Ta có thể phân biệt dễ dàng và trực quan 13 phân nhóm.

library(ggridges)

p1=df%>%ggplot(aes(y=dataset,x=y))+
  geom_density_ridges(aes(fill=dataset),
                      alpha=0.5,
                      scale=4,
                      stat = "binline",
                      bins=50,
                      show.legend = F)+
  theme_bw()

p2=df%>%ggplot(aes(x=dataset,y))+
  geom_jitter(aes(col=dataset),size=1,alpha=0.6,show.legend = F)+
  scale_x_discrete(breaks=NULL)+
  coord_flip()+
  theme_bw()

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

  1. Kernel density plot

Khi làm mượt histogram, ta sẽ có Density plot, tuy nhiên cũng chính vì lý do này, density plot dễ bị nhiễu hơn histogram. Cũng như violin plot, densityplot hoàn toàn bị đánh lừa với các phân phối đối xứng, multimodal và gây ảo giác về phân phối chuẩn. Do đó, densityplot kém hơn histogram.

library(ggridges)

p1=df%>%ggplot(aes(y=dataset,x=x))+
  geom_density_ridges(aes(fill=dataset),alpha=0.5,scale=3,show.legend = F)+
  theme_bw()

p2=df%>%ggplot(aes(x=dataset,y))+
  geom_jitter(aes(col=dataset),size=1,alpha=0.6,show.legend = F)+
  scale_x_discrete(breaks=NULL)+
  coord_flip()+
  theme_bw()

gridExtra::grid.arrange(p1,p2,ncol=2)
## Picking joint bandwidth of 5.46

6 Kết luận

Sau thí nghiệm nhỏ này, ta có thể rút ra một số kết luận như sau:

  1. Bộ dữ liệu datasaurus chỉ là một can thiệp mô phỏng, tận dụng tối đa hiện tượng “Hồi quy về trung bình” và thủ thuật gây nhiễu để đánh lừa mô hình Hồi quy Tuyến tính và các công thức Mean, SD cho từng biến số. Dù cách làm này có vẻ cực đoan và phi thực tế, tuy nhiên nó đã chứng minh được rằng các bảng kết quả thống kê mô tả như Mean, SD mà ta thường thấy trong các công bố khoa học đều đáng ngờ.

  2. Thống kê mô tả đơn giản bằng Mean và SD là không đủ để nói lên sự thật về cấu trúc dữ liệu. Để tránh nguy cơ bị đánh lừa bởi ảo giác, chúng ta cần phải khảo sát chi tiết hơn về đặc tính phân bố, thí dụ dùng thêm Min, Max, Skewness, Kurtosis, IQR và bách phân vị. Để an toàn hơn nữa, ta có thể sử dụng bootstrap.

  3. Trong mọi trường hợp, thống kê mô tả luôn phải kết hợp với biểu đồ trực quan. Data visualisation rất quan trọng, thậm chí quan trọng hơn các bảng thống kê, vì chỉ có biểu đồ mới cho phép chúng ta nhìn thấy sự thật.

  4. Cho mục tiêu mô tả đặc tính phân bố, ta có thể sử dụng nhiều giải pháp (dạng biểu đồ), tuy nhiên khả năng của mỗi dạng có thể khác nhau. Boxplot là dạng biểu đồ đơn giản và thông dụng nhất, nhưng nó cũng chính là phương pháp kém chính xác nhất. Violin plot chính xác và tinh tế hơn Boxplot. Histogram và scatter plot là 2 dạng biểu đồ chính xác nhất, gần với sự thực nhất. Density plot đẹp và giàu thông tin hơn boxplot, nhưng kém chính xác hơn Histogram.

Bài thực hành đến đây là kết thúc. Cảm ơn sự theo dõi của các bạn và hẹn gặp lại.

Tài liệu tham khảo:

Toàn bộ dataset có thể download tại đây:

https://www.autodeskresearch.com/sites/default/files/SameStatsDataAndImages.zip

  1. Cairo, A. Download the Datasaurus: Never trust summary statistics alone; always visualize your data. http://www.thefunctionalart.com/2016/08/downloaddatasaurus-never-trust-summary.html.

  2. Anscombe, F.J. (1973). Graphs in Statistical Analysis. The American Statistician 27, 1, 17–21.

  3. Blyth, C.R. (1972). On Simpson’s Paradox and the Sure-Thing Principle. Journal of the American Statistical Association 67, 338, 364–366.

  4. Justin Matejka, George Fitzmaurice. Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing. https://www.autodeskresearch.com/publications/samestats

LS0tDQp0aXRsZTogIkV4cGxvcmluZyBkYXRhc2F1cnVzIiANCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjAxIFRow6FuZyAxIDIwMTgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIVtdKGRhdGFzYXVydXMucG5nKQ0KDQojIEdp4bubaSB0aGnhu4d1DQoNCk5oxrAgY8OidSBjaHV54buHbiBuZ+G7pSBuZ8O0biAiVGjhuqd5IGLDs2kgc+G7nSB2b2kiLCBuaGnhu4F1IHTDoWMgZ2nhuqMgxJHDoyBkw7luZyBuaOG7r25nIHRow60gbmdoaeG7h20gbcO0IHBo4buPbmcgxJHhu4MgY+G6o25oIGLDoW8gduG7gSBuZ3V5IGPGoSBzYWkgbOG6p20gdsOgIG5oxrDhu6NjIMSRaeG7g20gY+G7p2EgY8OhYyBwaMawxqFuZyBwaMOhcCB0aOG7kW5nIGvDqiBxdXkgxrDhu5tjLCBiYW8gZ+G7k20gTWVhbiwgU2QsIFBlYXJzb24ncyByLCBCb3hwbG90Li4gDQoNClRow60gZOG7pTogDQoNCk5naOG7i2NoIGzDvSBTaW1wc29uIEUuSCAoMTk1MSkgbMOgIG3hu5l0IGNhbiB0aGnhu4dwIG5o4bqvbSB2w6BvIGjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gciBj4bunYSBQZWFyc29uOyB0aGVvIMSRw7MgOiBUcm9uZyBk4buvIGxp4buHdSDEkcaw4bujYyB0w6FpIGPhuqV1IHRyw7pjIG5oxrAgaMOsbmggQjogUsO1IHLDoG5nIGzDoCBZIHTGsMahbmcgcXVhbiBuZ2jhu4tjaCB24bubaSBYLCBuaMawbmcga+G6v3QgcXXhuqMgdGjhu5FuZyBrw6ogKEjDrG5oIEMpIGNobyByYSBnacOhIHRy4buLIHI+MCB2w6AgdMawxqFuZyDEkcawxqFuZyB24bubaSBt4buZdCBk4buvIGxp4buHdSBjw7MgdMawxqFuZyBxdWFuIHRodeG6rW4gdHJvbmcgaMOsbmggQS4NCg0KIVtdKFNpbXBzb25wYXJhZG94LnBuZykNCg0KTsSDbSAxOTczLCB0YSBiaeG6v3QgxJHhur9uIGLhu5kgdOG7qSBBbnNjb21iZSB24bubaSA0IGThu68gbGnhu4d1IHbhu5tpIGPhuqV1IHRyw7pjIGhvw6BuIHRvw6BuIGtow6FjIG5oYXUgbmjGsG5nIMSR4buBdSBjaG8gcmEgY8O5bmcga+G6v3QgcXXhuqMgVHJ1bmcgYsOsbmgsIGPDuW5nIMSR4buZIGzhu4djaCBjaHXhuqluIHbDoCBjw7luZyBo4buHIHPhu5EgdMawxqFuZyBxdWFuLiBExKkgbmhpw6puIHRhIGPDsyB0aOG7gyBwaOG7pyBuaOG6rW4gw70gdMaw4bufbmcgY+G7p2EgQW5zY29tYmUgdsOsIGNobyBy4bqxbmcgbsOzIHF1w6EgY+G7sWMgxJFvYW4uIE5oxrBuZyB2w6BvIG7Eg20gMjAxNiwgSnVzdGluIE1hdGVramEgdsOgIEdlb3JnZSBGaXR6bWF1cmljZSAoaHR0cHM6Ly93d3cuYXV0b2Rlc2tyZXNlYXJjaC5jb20vcHVibGljYXRpb25zL3NhbWVzdGF0cykgxJHDoyBz4butIGThu6VuZyBt4buZdCB0aHXhuq10IHRvw6FuIG3DtCBwaOG7j25nIMSR4buDIGhp4buHbiB0aOG7sWMgaMOzYSBi4buZIHThu6kgQW5zY29tYmUgdGhlbyBoxrDhu5tuZyBuZ+G6q3Ugbmhpw6puLCBwaGkgY+G6pXUgdHLDumMuIEvhur90IHF14bqjIGNobyB0aOG6pXkgY+G6oyA4IGLhu5kgZOG7ryBsaeG7h3UgbsOgeSDEkeG7gXUgY8OzIGNodW5nIGvhur90IHF14bqjIE1lYW4sIFNkLCBQZWFyc29uJ3Mgci4gS+G6v3QgcXXhuqMgbsOgeSBjaG8gdGjhuqV5IGzDoCBuZ3V5IGPGoSBuaOG6p20gbOG6q24gaG/DoG4gdG/DoG4gY8OzIHRo4buDIHjhuqN5IHJhIHRyw6puIHRo4buxYyB04bq/LiBN4buZdCBjw6FjaCBnaeG6o2kgdGjDrWNoIGNobyBuZ2jhu4tjaCBsw70gbsOgeSwgxJHDsyBsw6AgIlJlZ3Jlc3Npb24gdG93YXJkcyB0aGUgbWVhbnMiIGhheSBoaeG7h24gdMaw4bujbmcgSOG7k2kgcXV5IHbhu4EgdHJ1bmcgYsOsbmguIA0KDQohW10oQW5zY29tYmUucG5nKQ0KDQojIDEyIGJp4bq/biB0aOG7gyBEYXRhc2F1cnVzDQoNCkPFqW5nIHRyb25nIG7Eg20gMjAxNiwgQ2Fpcm8gQS4gY8OybiDEkWkgeGEgaMahbiBu4buvYSBraGkgZ2nhu5tpIHRoaeG7h3Uga2jDtG5nIGNo4buJIDQsIG3DoCDEkeG6v24gMTIgYmnhur9uIHRo4buDIGThu68gbGnhu4d1LiBUaMOtIG5naGnhu4dtIG7hu5VpIHRp4bq/bmcgbsOgeSBjw7MgdMOqbiBsw6AgRGF0YXNhdXJ1cyBEb3plbi4gVHJvbmcgYsOgaSBuw6B5LCBOaGkgc+G6vSBjw7luZyBjw6FjIGLhuqFuIGto4bqjbyBzw6F0IGLhu5kgZOG7ryBsaeG7h3UgbsOgeS4NCg0KVHLGsOG7m2MgaOG6v3QgdGEgdOG6o2kgZmlsZSBEYXRhc2F1cnVzIHThu6sgxJHDonk6IGh0dHBzOi8vd3d3LmF1dG9kZXNrcmVzZWFyY2guY29tL3NpdGVzL2RlZmF1bHQvZmlsZXMvVGhlJTIwRGF0YXNhdXJ1cyUyMERvemVuLnppcA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpkZj1yZWFkX3RzdigiRGF0YXNhdXJ1c0RvemVuLnRzdiIpJT4lYXNfdGliYmxlKCkNCmBgYA0KDQojIEPDoWMgdHLhu4sgc+G7kSB0aOG7kW5nIGvDqiBtw7QgdOG6ow0KDQpC4buZIHPhu5EgbGnhu4d1IGPDsyBk4bqhbmcgImxvbmcgZm9ybWF0IiwgZ+G7k20gMyBiaeG6v24sIGJp4bq/biBkYXRhc2V0IGNobyBwaMOpcCBwaMOibiBjaGlhIGRhdGFmcmFtZSB0aMOgbmggMTMgbmjDs20ga2jDoWMgbmhhdSwgbeG7l2kgYuG7mSBk4buvIGxp4buHdSBuaOG7jyBuw6B5IGPDsyAyIGJp4bq/biBsacOqbiB04bulYyBYIHbDoCBZLCB24bubaSBz4buRIGzGsOG7o25nIG5oxrAgbmhhdSAobj0xNDIpLg0KDQpUaOG7kW5nIGvDqiBtw7QgdOG6oyB24bubaSBNZWFuIHbDoCBTRCBjaG8gdGjhuqV5IGPhuqMgMTMgYuG7mSBz4buRIGxp4buHdSDEkeG7gXUgY8OzIE1lYW4gWCA9IDU0LjI3IHbDoCBTRCBj4bunYSBYID0gMTYuNzcNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRmICU+JQ0KICBncm91cF9ieShkYXRhc2V0KSAlPiUNCiAgc3VtbWFyaXNlX2F0KCJ4IixmdW5zKFNpemU9bGVuZ3RoLA0KICAgICAgICAgICAgICAgICAgICAgICAgTWVhbj1tZWFuLA0KICAgICAgICAgICAgICAgICAgICAgICAgU0Q9c2QpKSU+JWtuaXRyOjprYWJsZSgpDQoNCmBgYA0KDQpMw6BtIHTGsMahbmcgdOG7sSBjaG8gYmnhur9uIFksIHRhIHPhur0gdGjhuqV5IGvhur90IHF14bqjIGzDoCBNZWFuIFk9NDcuODMgdsOgIFNEIFk9MjYuOTQsIG5oxrAgbmhhdSBjaG8gMTMgbmjDs20uDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGYgJT4lDQogIGdyb3VwX2J5KGRhdGFzZXQpICU+JQ0KICBzdW1tYXJpc2VfYXQoInkiLGZ1bnMoU2l6ZT1sZW5ndGgsDQogICAgICAgICAgICAgICAgICAgICAgICBNZWFuPW1lYW4sDQogICAgICAgICAgICAgICAgICAgICAgICBTRD1zZCkpJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpQaMOibiB0w61jaCB0xrDGoW5nIHF1YW4gZ2nhu69hIFkgdsOgIFggY2hvIHRo4bqleSBj4bqjIDEzIG5ow7NtIMSR4buBdSBjw7MgaOG7hyBz4buRIHTGsMahbmcgcXVhbiBQZWFyc29uJ3MgciBy4bqldCBn4bqnbiBuaGF1LCBraG/huqNuZyB04burIC0wLjA2MyDEkeG6v24gLTAuMDY5DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGYlPiVzcGxpdCguJGRhdGFzZXQpJT4lbWFwKH5jb3IoLiR4LC4keSxtZXRob2Q9InBlYXJzb24iKSklPiVjYmluZCgpDQpgYGANCg0KWGEgaMahbiBu4buvYSwgdGEgY8OzIHRo4buDIHRo4bqleSBsw6AgMTMgbcO0IGjDrG5oIGjhu5NpIHF1eSBZflggxJHhu4F1IGPDsyBu4buZaSBkdW5nIG5oxrAgbmhhdToNCg0KYGBge3J9DQpkZiU+JXNwbGl0KC4kZGF0YXNldCklPiUNCiAgbWFwKH5icm9vbTo6dGlkeShsbSguJHl+LiR4KSkpDQpgYGANCg0KIyBT4buxIHRo4bqtdCB24buBIERhdGFzYXVydXMNCg0KU+G7sSB0aOG6rXQgduG7gSBj4bqldSB0csO6YyBj4bunYSAxMyBi4buZIHPhu5EgbGnhu4d1IG7DoHkgY2jhu4kgxJHGsOG7o2MgcGjGoWkgYsOgeSBraGkgdGEgbcO0IHThuqMgdHLhu7FjIHF1YW4gduG7m2kgYmnhu4N1IMSR4buTIHTDoW4geOG6oS4gQuG6oW4gc+G6vSBi4bqldCBuZ+G7nSB2w6wgMTMgaMOsbmgg4bqjbmggaG/DoG4gdG/DoG4ga2jDoWMgYmnhu4d0Og0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmRmJT4lZ2dwbG90KGFlcyh4LHkpKSsNCiAgZ2VvbV9wb2ludChhZXMoY29sPWRhdGFzZXQpLHNpemU9MSkrDQogIHRoZW1lX2J3KDgpKw0KICBjb29yZF9lcXVhbCgpKw0KICBmYWNldF93cmFwKH5kYXRhc2V0LG5jb2w9NCxzY2FsZXM9ImZyZWUiKQ0KYGBgDQoNCk5oxrAgduG6rXksIGNo4bqvYyBjw6FjIGLhuqFuIGPFqW5nIMSR4buTbmcgw70gcuG6sW5nIG5o4buvbmcgdHLhu4sgc+G7kSBUcnVuZyBiw6xuaCB2w6AgU0QgaG/DoG4gdG/DoG4gYuG7iyBoYWNrICjEkcOhbmggbOG7q2EpLCBjaMO6bmcga2jDtG5nIMSR4bunIMSR4buDIG3DtCB04bqjIHRydW5nIHRo4buxYyB24buBIMSR4bq3YyB0w61uaCBwaMOibiBi4buRIGPhu6dhIGThu68gbGnhu4d1LiANCg0KQ8OidSBo4buPaSDEkeG6t3QgcmEgxJHDsyBsw6A6IE7hur91IE1lYW4gdsOgIFNEIGPDsyBuZ3V5IGPGoSBzYWkgbOG6p20gY2FvIG5oxrAgduG6rXksIGxp4buHdSB0YSBjw7MgdGjhu4MgdHLDoW5oIG5oxrBuZyDhuqNvIGdpw6FjIG7DoHkgYuG6sW5nIG5o4buvbmcgcGjDqXAgdGjhu5FuZyBrw6ogbcO0IHThuqMga2jDoWMgaGF5IGtow7RuZyA/DQoNCsSQ4buDIHRy4bqjIGzhu51pLCBOaGkgc+G6vSBs4bqnbiBsxrDhu6N0IHRo4butIG5naGnhu4dtIG5o4buvbmcgY8OhY2ggbMOgbSBraMOhYyBuaGF1Og0KDQoxKSDEkOG6p3UgdGnDqm4sIHRhIHPhu60gZOG7pW5nIHRow6ptIE1lZGlhbiwgSVFSLCBTa2V3bmVzcywga3VydG9zaXMgY2hvIGJp4bq/biBZDQoNCkvhur90IHF14bqjIGNobyB0aOG6pXkgTWVkaWFuLCBTa2V3bmVzcywgS3VydG9zaXMgdsOgIElRUiBraMO0bmcgYuG7iyDhuqNuaCBoxrDhu59uZyBi4bufaSB0aOG7pyB0aHXhuq10IGNhbiB0aGnhu4dwIHRyb25nIDEzIGLhu5kgZOG7ryBsaeG7h3UgZGF0YXNhdXJ1cy4NCg0KYGBge3J9DQoNCmxpYnJhcnkoZTEwNzEpDQoNCmRmICU+JQ0KICBncm91cF9ieShkYXRhc2V0KSAlPiUNCiAgc3VtbWFyaXNlX2F0KCJ5IixmdW5zKE1lYW49bWVhbiwNCiAgICAgICAgICAgICAgICAgICAgICAgIE1lZGlhbj1tZWRpYW4sDQogICAgICAgICAgICAgICAgICAgICAgICBTa2V3bmVzcz1za2V3bmVzcywNCiAgICAgICAgICAgICAgICAgICAgICAgIEt1cnRvc2lzPWt1cnRvc2lzLA0KICAgICAgICAgICAgICAgICAgICAgICAgSVFSPUlRUikpJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQoyKSBCw6FjaCBwaMOibiB24buLLCBNaW4gdsOgIE1heA0KDQpUxrDGoW5nIHThu7EsIGto4bqjbyBzw6F0IGLDoWNoIHBow6JuIHbhu4sgY8WpbmcgY2hvIHBow6lwIG3DtCB04bqjIHRydW5nIHRo4buxYyBuaOG6pXQgxJHhurdjIHTDrW5oIHBow6JuIGLhu5EgY+G7p2EgYmnhur9uIFkgDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGdyb3VwX2J5KGRhdGFzZXQpICU+JQ0KICBzdW1tYXJpc2VfYXQoIngiLGZ1bnMoTWluPW1pbixNYXg9bWF4LA0KICAgICAgICAgICAgICAgICAgICAgICAgUTU9cXVhbnRpbGUoLixwcm9icz0wLjA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIFEyNT1xdWFudGlsZSguLHByb2JzPTAuNTUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgTWVkaWFuPXF1YW50aWxlKC4scHJvYnM9MC41KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIFE3NT1xdWFudGlsZSguLHByb2JzPTAuNzUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgUTk1PXF1YW50aWxlKC4scHJvYnM9MC45NSkpKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KMykgQm9vdHN0cmFwIHRydW5nIGLDrG5oDQoNClRhIGPFqW5nIGPDsyB0aOG7gyDDoXAgZOG7pW5nIG3hu5l0IHBow6JuIHTDrWNoIHRydW5nIGLDrG5oIGvDqG0gdGhlbyBib290c3RyYXAuIFZp4buHYyBjaOG7jW4gbeG6q3Ugbmfhuqt1IG5oacOqbiBjw7MgdGjhu4Mga2jhuq9jIHBo4bulYyBuZ3V5IGPGoSBzYWkgbOG6p206IEdpw6EgdHLhu4sgTWVhbiB0w61uaCDEkcaw4bujYyBjaG8geSBzYXUgMTAwIGzhuqduIGJvb3RzdHJhcCBraMO0bmcgY8OybiDEkeG7k25nIG5o4bqldCBuaMawIGJhbiDEkeG6p3U6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpsaWJyYXJ5KGJyb29tKQ0KDQpzZXQuc2VlZCgxMjM0KQ0KDQpib290bWVhbj1mdW5jdGlvbihkYXRhLG4pew0KICBkYXRhJT4lYm9vdHN0cmFwKG4pJT4lDQogIGRvKHRpZHkobG0oLiR5fjEpKSktPnRlbXANCiAgbWVhbih0ZW1wJGVzdGltYXRlKQ0KfQ0KDQpkZiU+JXNwbGl0KC4kZGF0YXNldCklPiVtYXAofmJvb3RtZWFuKC4sbj0xMDApKSU+JWNiaW5kKCkNCg0KYGBgDQoNCjQpIFTGsMahbmcgcXVhbiBwaGkgdGhhbSBz4buROiANCg0KVsOsIGThu68gbGnhu4d1IFggdsOgIFkgxJHhu4F1IGPDsyBwaMOibiBi4buRIGLhuqV0IHRoxrDhu51uZywgZG8gxJHDsyBraGkgdGEgc+G7rSBk4bulbmcgcGjGsMahbmcgcGjDoXAgdMawxqFuZyBxdWFuIFNwZWFybWFuJ3MgcmhvLCBr4bq/dCBxdeG6oyBz4bq9IGtow6FjIGjhurNuIHNvIHbhu5tpIFBlYXJzb24ncyByLg0KDQpgYGB7cn0NCmRmJT4lc3BsaXQoLiRkYXRhc2V0KSU+JW1hcCh+Y29yKC4keCwuJHksbWV0aG9kPSJzcGVhcm1hbiIpKSU+JWNiaW5kKCkNCg0KYGBgDQoNCiMgTcO0IHThuqMgdHLhu7FjIHF1YW4NCg0KS+G6v3QgbHXhuq1uIGhp4buDbiBuaGnDqm4gdOG7qyAyRCBzY2F0dGVyIHBsb3QgY+G7p2EgRGF0YXNhdXJ1cyBjaG8gdGjhuqV5IHZp4buHYyBtw7QgdOG6oyB0cuG7sWMgcXVhbiBjw7MgdmFpIHRyw7IgcuG6pXQgcXVhbiB0cuG7jW5nLCBz4buRIGxp4buHdSB0aOG7kW5nIGvDqiBsw6AgaMawIOG6o28sIG5oxrBuZyBiaeG7g3UgxJHhu5MgbHXDtG4gbsOzaSBsw6puIHPhu7EgdGjhuq10LiBUdXkgbmhpw6puLCBOaGkgbXXhu5FuIHTDrG0gaGnhu4N1IHhhIGjGoW4gbeG7mXQgY2jDunQsIHbhu5tpIGPDonUgaOG7j2k6IE5nb8OgaSAyRCBwbG90LCBraGkgbXXhu5FuIG3DtCB04bqjIMSR4bq3YyB0w61uaCBwaMOibiBi4buRIDEgY2hp4buBdSAoxJHGoW4gYmnhur9uKSwgZOG6oW5nIGJp4buDdSDEkeG7kyBuw6BvIGzDoCB04buRdCBuaOG6pXQgPw0KDQpUYSBz4bq9IGzhuqduIGzGsOG7o3QgdGjhu60gY8OhYyBnaeG6o2kgcGjDoXAgc2F1IMSRw6J5IGNobyBiaeG6v24gWToNCg0KMSkgUG9pbnQgcmFuZ2UgKMSQaeG7g20gdsOgIGVycm9yIGJhcikNCg0KS+G6v3QgcXXhuqMgY2hvIHRo4bqleSBQb2ludCByYW5nZSBwbG90IGzDoCBk4bqhbmcgYmnhu4N1IGThu5MgxJHGoW4gZ2nhuqNuIG5o4bqldCwgbmjGsG5nIG7DsyBob8OgbiB0b8OgbiB0aOG6pXQgYuG6oWkgdsOgIGLhu4sgxJHDoW5oIGzhu6thIDoNCg0KYGBge3J9DQpkZiU+JWdncGxvdChhZXMoeD1kYXRhc2V0LHkpKSsNCiAgc3RhdF9zdW1tYXJ5KGFlcyhjb2w9ZGF0YXNldCksDQogICAgICAgICAgICAgICBmdW4uZGF0YSA9ICJtZWFuX2NsX2Jvb3QiLA0KICAgICAgICAgICAgICAgc2l6ZT0xKSt0aGVtZV9idygpK2Nvb3JkX2ZsaXAoKQ0KDQpgYGANCg0KMikgQm94cGxvdDoNCg0KQm94cGxvdCBsw6AgZOG6oW5nIGJp4buDdSDEkeG7kyBy4bqldCB0aMO0bmcgZOG7pW5nIMSR4buDIG3DtCB04bqjIMSR4bq3YyB0w61uaCBwaMOibiBi4buRIGNobyAxIGJp4bq/bi4gVHV5IG5oacOqbiwgY2jDrW5oIG5ndXnDqm4gbMO9IMSRxqFuIGdp4bqjbiAoY2jhu4kgc+G7rSBk4bulbmcgNSB0aMO0bmcgdGluKSB0cm9uZyBCb3hwbG90IGtoaeG6v24gbsOzIGPDsyBuZ3V5IGPGoSBi4buLIG5n4buZIG5o4bqtbiBjYW8uIEtoaSDDoXAgZOG7pW5nIGNobyAxMyBi4buZIGThu68gbGnhu4d1IERhdGFzYXVydXMsIGto4bqjIG7Eg25nIHBow6JuIGJp4buHdCBj4bunYSBCb3hwbG90IHLhuqV0IGvDqW0sIGjGoW4gbuG7r2EgbsOzIGPDsm4gZ8OieSDhuqNvIGdpw6FjIHbhu4EgcGjDom4gYuG7kSBjaHXhuqluLCBsacOqbiB04bulYyBj4bunYSBiaeG6v24gWSB0cm9uZyBt4buZdCBz4buRIHRyxrDhu51uZyBo4bujcCBuaMawIG5ow7NtIGhfbGluZXMsIGRvdHMsIGNpcmNsZSwgZGluby4NCg0KYGBge3J9DQpwMT1kZiU+JWdncGxvdChhZXMoeD1kYXRhc2V0LHk9eSkpKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZGF0YXNldCksYWxwaGE9MC41LHNob3cubGVnZW5kID0gRikrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoKQ0KDQpwMj1kZiU+JWdncGxvdChhZXMoeD1kYXRhc2V0LHk9eSkpKw0KICBnZW9tX2ppdHRlcihhZXMoY29sPWRhdGFzZXQpLHNpemU9MSxhbHBoYT0wLjYsc2hvdy5sZWdlbmQgPSBGKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3M9TlVMTCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoKQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSxwMixuY29sPTIpDQoNCmBgYA0KDQozKSBWaW9saW4gcGxvdDoNCg0KVmlvbGluIHBsb3QgY8OzIMawdSB0aOG6vyBjYW8gaMahbiBo4bqzbiBzbyB24bubaSBib3hwbG90LCB0cm9uZyB0csaw4budbmcgaOG7o3AgbsOgeSBu4bq/dSBz4butIGThu6VuZyB2aW9saW4gcGxvdCBsw6AgY8OzIHRo4buDIHBow6JuIGJp4buHdCBk4buFIGTDoG5nIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkgY+G7p2EgWSBnaeG7r2EgMTMgYuG7mSBz4buRIGxp4buHdS4gTmjGsOG7o2MgxJFp4buDbSBkdXkgbmjhuqV0IGPhu6dhIHZpb2xpbiBwbG90IGzDoCBuw7MgduG6q24gY8OybiBi4buLIMSRw6FuaCBs4burYSBi4bufaSBj4bqldSB0csO6YyDEkeG7kWkgeOG7qW5nIHRyb25nIGPDoWMgbmjDs20gaGxpbmVzIHbDoCBidWxsc2V5ZTsga+G6v3QgcXXhuqMgc+G6vSBnw6J5IOG6o28gZ2nDoWMgduG7gSBt4buZdCBwaMOibiBwaOG7kWkgdW5pZm9ybSB0cm9uZyBraGkgc+G7sSB0aOG6rXQga2jDtG5nIHBo4bqjaSBuaMawIHbhuq15Lg0KDQpgYGB7cn0NCnAxPWRmJT4lZ2dwbG90KGFlcyh4PWRhdGFzZXQseSkpKw0KICBnZW9tX3Zpb2xpbihhZXMoZmlsbD1kYXRhc2V0KSxhbHBoYT0wLjUsc2hvdy5sZWdlbmQgPSBGKSsNCiAgY29vcmRfZmxpcCgpKw0KICB0aGVtZV9idygpDQoNCnAyPWRmJT4lZ2dwbG90KGFlcyh4PWRhdGFzZXQseSkpKw0KICBnZW9tX2ppdHRlcihhZXMoY29sPWRhdGFzZXQpLHNpemU9MSxhbHBoYT0wLjYsc2hvdy5sZWdlbmQgPSBGKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3M9TlVMTCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoKQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSxwMixuY29sPTIpDQpgYGANCg0KNCkgSGlzdG9ncmFtDQoNCkhpc3RvZ3JhbSBsw6AgZOG6oW5nIGJp4buDdSDEkeG7kyB04buRdCBuaOG6pXQgxJHhu4MgbcO0IHThuqMgxJHhurdjIHTDrW5oIHBow6JuIHBo4buRaSDEkcahbiBiaeG6v24uIFRo4bqtdCB24bqteSwgaGlzdG9ncmFtIGN1bmcgY+G6pXAgaMOsbmgg4bqjbmggdHJ1bmcgdGjhu7FjIG5o4bqldCwgaMahbiBu4buvYSAtIGPDsyB0aOG7gyB0aW5oIGNo4buJbmggdMO5eSDDvSAtIHbhu4EgbeG6rXQgxJHhu5kgcGjDom4gYuG7kS5UYSBjw7MgdGjhu4MgcGjDom4gYmnhu4d0IGThu4UgZMOgbmcgdsOgIHRy4buxYyBxdWFuIDEzIHBow6JuIG5ow7NtLiANCg0KYGBge3J9DQpsaWJyYXJ5KGdncmlkZ2VzKQ0KDQpwMT1kZiU+JWdncGxvdChhZXMoeT1kYXRhc2V0LHg9eSkpKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyhmaWxsPWRhdGFzZXQpLA0KICAgICAgICAgICAgICAgICAgICAgIGFscGhhPTAuNSwNCiAgICAgICAgICAgICAgICAgICAgICBzY2FsZT00LA0KICAgICAgICAgICAgICAgICAgICAgIHN0YXQgPSAiYmlubGluZSIsDQogICAgICAgICAgICAgICAgICAgICAgYmlucz01MCwNCiAgICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYpKw0KICB0aGVtZV9idygpDQoNCnAyPWRmJT4lZ2dwbG90KGFlcyh4PWRhdGFzZXQseSkpKw0KICBnZW9tX2ppdHRlcihhZXMoY29sPWRhdGFzZXQpLHNpemU9MSxhbHBoYT0wLjYsc2hvdy5sZWdlbmQgPSBGKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3M9TlVMTCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoKQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSxwMixuY29sPTIpDQpgYGANCg0KNSkgS2VybmVsIGRlbnNpdHkgcGxvdA0KDQpLaGkgbMOgbSBtxrDhu6N0IGhpc3RvZ3JhbSwgdGEgc+G6vSBjw7MgRGVuc2l0eSBwbG90LCB0dXkgbmhpw6puIGPFqW5nIGNow61uaCB2w6wgbMO9IGRvIG7DoHksIGRlbnNpdHkgcGxvdCBk4buFIGLhu4sgbmhp4buFdSBoxqFuIGhpc3RvZ3JhbS4gQ8WpbmcgbmjGsCB2aW9saW4gcGxvdCwgZGVuc2l0eXBsb3QgaG/DoG4gdG/DoG4gYuG7iyDEkcOhbmggbOG7q2EgduG7m2kgY8OhYyBwaMOibiBwaOG7kWkgxJHhu5FpIHjhu6luZywgbXVsdGltb2RhbCB2w6AgZ8OieSDhuqNvIGdpw6FjIHbhu4EgcGjDom4gcGjhu5FpIGNodeG6qW4uIERvIMSRw7MsIGRlbnNpdHlwbG90IGvDqW0gaMahbiBoaXN0b2dyYW0uDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3JpZGdlcykNCg0KcDE9ZGYlPiVnZ3Bsb3QoYWVzKHk9ZGF0YXNldCx4PXgpKSsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoZmlsbD1kYXRhc2V0KSxhbHBoYT0wLjUsc2NhbGU9MyxzaG93LmxlZ2VuZCA9IEYpKw0KICB0aGVtZV9idygpDQoNCnAyPWRmJT4lZ2dwbG90KGFlcyh4PWRhdGFzZXQseSkpKw0KICBnZW9tX2ppdHRlcihhZXMoY29sPWRhdGFzZXQpLHNpemU9MSxhbHBoYT0wLjYsc2hvdy5sZWdlbmQgPSBGKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3M9TlVMTCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgdGhlbWVfYncoKQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSxwMixuY29sPTIpDQpgYGANCg0KIyBL4bq/dCBsdeG6rW4NCg0KU2F1IHRow60gbmdoaeG7h20gbmjhu48gbsOgeSwgdGEgY8OzIHRo4buDIHLDunQgcmEgbeG7mXQgc+G7kSBr4bq/dCBsdeG6rW4gbmjGsCBzYXU6DQoNCjEpIELhu5kgZOG7ryBsaeG7h3UgZGF0YXNhdXJ1cyBjaOG7iSBsw6AgbeG7mXQgY2FuIHRoaeG7h3AgbcO0IHBo4buPbmcsIHThuq1uIGThu6VuZyB04buRaSDEkWEgaGnhu4duIHTGsOG7o25nICJI4buTaSBxdXkgduG7gSB0cnVuZyBiw6xuaCIgdsOgIHRo4bunIHRodeG6rXQgZ8OieSBuaGnhu4V1IMSR4buDIMSRw6FuaCBs4burYSBtw7QgaMOsbmggIEjhu5NpIHF1eSBUdXnhur9uIHTDrW5oIHbDoCBjw6FjIGPDtG5nIHRo4bupYyBNZWFuLCBTRCBjaG8gdOG7q25nIGJp4bq/biBz4buRLiBEw7kgY8OhY2ggbMOgbSBuw6B5IGPDsyB24bq7IGPhu7FjIMSRb2FuIHbDoCBwaGkgdGjhu7FjIHThur8sIHR1eSBuaGnDqm4gbsOzIMSRw6MgY2jhu6luZyBtaW5oIMSRxrDhu6NjIHLhurFuZyBjw6FjIGLhuqNuZyBr4bq/dCBxdeG6oyB0aOG7kW5nIGvDqiBtw7QgdOG6oyBuaMawIE1lYW4sIFNEIG3DoCB0YSB0aMaw4budbmcgdGjhuqV5IHRyb25nIGPDoWMgY8O0bmcgYuG7kSBraG9hIGjhu41jIMSR4buBdSDEkcOhbmcgbmfhu50uIA0KDQoyKSBUaOG7kW5nIGvDqiBtw7QgdOG6oyDEkcahbiBnaeG6o24gYuG6sW5nIE1lYW4gdsOgIFNEIGzDoCBraMO0bmcgxJHhu6cgxJHhu4MgbsOzaSBsw6puIHPhu7EgdGjhuq10IHbhu4EgY+G6pXUgdHLDumMgZOG7ryBsaeG7h3UuIMSQ4buDIHRyw6FuaCBuZ3V5IGPGoSBi4buLIMSRw6FuaCBs4burYSBi4bufaSDhuqNvIGdpw6FjLCBjaMO6bmcgdGEgY+G6p24gcGjhuqNpIGto4bqjbyBzw6F0IGNoaSB0aeG6v3QgaMahbiB24buBIMSR4bq3YyB0w61uaCBwaMOibiBi4buRLCB0aMOtIGThu6UgZMO5bmcgdGjDqm0gTWluLCBNYXgsIFNrZXduZXNzLCBLdXJ0b3NpcywgSVFSIHbDoCBiw6FjaCBwaMOibiB24buLLiDEkOG7gyBhbiB0b8OgbiBoxqFuIG7hu69hLCB0YSBjw7MgdGjhu4Mgc+G7rSBk4bulbmcgYm9vdHN0cmFwLg0KDQozKSBUcm9uZyBt4buNaSB0csaw4budbmcgaOG7o3AsIHRo4buRbmcga8OqIG3DtCB04bqjIGx1w7RuIHBo4bqjaSBr4bq/dCBo4bujcCB24bubaSBiaeG7g3UgxJHhu5MgdHLhu7FjIHF1YW4uIERhdGEgdmlzdWFsaXNhdGlvbiBy4bqldCBxdWFuIHRy4buNbmcsIHRo4bqtbSBjaMOtIHF1YW4gdHLhu41uZyBoxqFuIGPDoWMgYuG6o25nIHRo4buRbmcga8OqLCB2w6wgY2jhu4kgY8OzIGJp4buDdSDEkeG7kyBt4bubaSBjaG8gcGjDqXAgY2jDum5nIHRhIG5ow6xuIHRo4bqleSBz4buxIHRo4bqtdC4NCg0KNCkgQ2hvIG3hu6VjIHRpw6p1IG3DtCB04bqjIMSR4bq3YyB0w61uaCBwaMOibiBi4buRLCB0YSBjw7MgdGjhu4Mgc+G7rSBk4bulbmcgbmhp4buBdSBnaeG6o2kgcGjDoXAgKGThuqFuZyBiaeG7g3UgxJHhu5MpLCB0dXkgbmhpw6puIGto4bqjIG7Eg25nIGPhu6dhIG3hu5dpIGThuqFuZyBjw7MgdGjhu4Mga2jDoWMgbmhhdS4gQm94cGxvdCBsw6AgZOG6oW5nIGJp4buDdSDEkeG7kyDEkcahbiBnaeG6o24gdsOgIHRow7RuZyBk4bulbmcgbmjhuqV0LCBuaMawbmcgbsOzIGPFqW5nIGNow61uaCBsw6AgcGjGsMahbmcgcGjDoXAga8OpbSBjaMOtbmggeMOhYyBuaOG6pXQuIFZpb2xpbiBwbG90IGNow61uaCB4w6FjIHbDoCB0aW5oIHThur8gaMahbiBCb3hwbG90LiBIaXN0b2dyYW0gIHbDoCBzY2F0dGVyIHBsb3QgbMOgIDIgZOG6oW5nIGJp4buDdSDEkeG7kyBjaMOtbmggeMOhYyBuaOG6pXQsIGfhuqduIHbhu5tpIHPhu7EgdGjhu7FjIG5o4bqldC4gRGVuc2l0eSBwbG90IMSR4bq5cCB2w6AgZ2nDoHUgdGjDtG5nIHRpbiBoxqFuIGJveHBsb3QsIG5oxrBuZyBrw6ltIGNow61uaCB4w6FjIGjGoW4gSGlzdG9ncmFtLg0KDQpCw6BpIHRo4buxYyBow6BuaCDEkeG6v24gxJHDonkgbMOgIGvhur90IHRow7pjLiBD4bqjbSDGoW4gc+G7sSB0aGVvIGTDtWkgY+G7p2EgY8OhYyBi4bqhbiB2w6AgaOG6uW4gZ+G6t3AgbOG6oWkuDQoNCioqVMOgaSBsaeG7h3UgdGhhbSBraOG6o286KioNCg0KVG/DoG4gYuG7mSBkYXRhc2V0IGPDsyB0aOG7gyBkb3dubG9hZCB04bqhaSDEkcOieToNCg0KPGh0dHBzOi8vd3d3LmF1dG9kZXNrcmVzZWFyY2guY29tL3NpdGVzL2RlZmF1bHQvZmlsZXMvU2FtZVN0YXRzRGF0YUFuZEltYWdlcy56aXA+DQoNCjEpIENhaXJvLCBBLiBEb3dubG9hZCB0aGUgRGF0YXNhdXJ1czogTmV2ZXIgdHJ1c3Qgc3VtbWFyeSBzdGF0aXN0aWNzIGFsb25lOyBhbHdheXMgdmlzdWFsaXplIHlvdXIgZGF0YS4gaHR0cDovL3d3dy50aGVmdW5jdGlvbmFsYXJ0LmNvbS8yMDE2LzA4L2Rvd25sb2FkZGF0YXNhdXJ1cy1uZXZlci10cnVzdC1zdW1tYXJ5Lmh0bWwuDQoNCjIpIEFuc2NvbWJlLCBGLkouICgxOTczKS4gR3JhcGhzIGluIFN0YXRpc3RpY2FsIEFuYWx5c2lzLiBUaGUgQW1lcmljYW4gU3RhdGlzdGljaWFuIDI3LCAxLCAxN+KAkzIxLg0KDQozKSBCbHl0aCwgQy5SLiAoMTk3MikuIE9uIFNpbXBzb27igJlzIFBhcmFkb3ggYW5kIHRoZSBTdXJlLVRoaW5nIFByaW5jaXBsZS4gSm91cm5hbCBvZiB0aGUgQW1lcmljYW4gU3RhdGlzdGljYWwgQXNzb2NpYXRpb24gNjcsIDMzOCwgMzY04oCTMzY2Lg0KDQo0KSBKdXN0aW4gTWF0ZWprYSwgR2VvcmdlIEZpdHptYXVyaWNlLiBTYW1lIFN0YXRzLCBEaWZmZXJlbnQgR3JhcGhzOiBHZW5lcmF0aW5nIERhdGFzZXRzIHdpdGggVmFyaWVkIEFwcGVhcmFuY2UgYW5kIElkZW50aWNhbCBTdGF0aXN0aWNzIHRocm91Z2ggU2ltdWxhdGVkIEFubmVhbGluZy4gaHR0cHM6Ly93d3cuYXV0b2Rlc2tyZXNlYXJjaC5jb20vcHVibGljYXRpb25zL3NhbWVzdGF0cyANCg0KDQo=