1 Giới thiệu

Phân tích trực quan bằng đồ họa thống kê là một giải pháp mô tả dữ liệu với hiệu quả cao hơn hẳn so với hình thức dùng bảng và con số. Tuy nhiên, đa số giáo trình/tài liệu trong trường lớp hiện nay còn khiếm khuyết rất nhiều về mảng biến định tính/rời rạc (categorical/discrete variables). Thí dụ, cho mục tiêu mô tả hoặc khảo sát mối tương quan giữa các biến định tính : các công cụ phổ thông mới chỉ cho phép trình bày thông tin 1 chiều (biểu đồ bánh) hoặc 2 chiều (biểu đồ hộp).

Trong năm 2018 này, Nhi sẽ phát hành một quyển sách chuyên đề về « Đồ họa thống kê », trong đó sẽ giới thiệu về tất cả những dạng biểu đồ thống kê cho 3 ứng dụng : mô tả đặc tính phân bố, so sánh trực quan, phân tích tương quan, cho cả biến định lượng và định tính.

Trong bài thực hành hôm nay, Nhi sẽ trích một phần nhỏ nôi dung chương 3 có chủ đề : « phân tích quan hệ giữa nhiều biến định tính » để giới thiệu với các bạn 6 công cụ đồ họa chuyên dụng gồm :

  1. Các biến thể của biểu đồ thanh (Bar graph)

  2. Biểu đồ khảm (Mosaic plot)

  3. Biểu đồ lưu lượng (Flow diagram)

  4. Biểu đồ dây cung (Chord diagram)

  5. Sơ đồ Venn

  6. Biểu đồ mạng từ luật kết hợp

Những công cụ này có thể được dùng để mô tả trực quan bảng chéo đa chiều để khảo sát quan hệ từ 3 biến định tính trở lên

Trước hết, ta sẽ tạo 1 dữ liệu mô phỏng, có nội dung khảo sát tần suất của 4 bệnh lý khác nhau (A,B,C,D) trong một mẫu 500 bệnh nhân. Hiện tượng nhiều bệnh cùng hiện diện trên một cá thể được gọi là comorbidity trong y học lâm sàng (thí dụ một người đàn ông có thể mắc bệnh COPD kết hợp với Suy tim và Tiểu đường).

library(tidyverse)
## -- Attaching packages ------------------------------------- tidyverse 1.2.1 --
## v ggplot2 2.2.1     v purrr   0.2.4
## v tibble  1.4.2     v dplyr   0.7.4
## v tidyr   0.8.0     v stringr 1.3.1
## v readr   1.1.1     v forcats 0.3.0
## -- Conflicts ---------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
set.seed(1705)

dat=data_frame(Benhnhan=paste("BN",c(1:500),sep=""),
               BenhA=sample(c("Y","N"),500, replace=T,prob=c(0.3,0.7))%>%as.factor(),
               BenhB=sample(c("Y","N"),500, replace=T,prob=c(0.55,0.45))%>%as.factor(),
               BenhC=sample(c("Y","N"),500, replace=T,prob=c(0.2,0.8))%>%as.factor(),
               BenhD=sample(c("Y","N"),500, replace=T,prob=c(0.6,0.4))%>%as.factor()
)

head(dat)%>%knitr::kable()
Benhnhan BenhA BenhB BenhC BenhD
BN1 N Y N Y
BN2 N Y N Y
BN3 Y Y N Y
BN4 N Y N Y
BN5 N Y N Y
BN6 Y Y Y N

Thống kê thường quy thường sử dụng một bảng chéo để mô tả sự phân bố tần suất/tỉ lệ của tất cả các tổ hợp có thể giữa 2 hay nhiều biến định tính.Khi có sự chia sẻ chung một tỉ lệ cao của một bậc (trạng thái) giữa 2 biến, ta nói 2 biến này có sự liên hệ với nhau, thí dụ một triệu chứng X xuất hiện (X=Yes) phổ biến ở các bệnh nhân (D=Yes). Một kiểm định Chi-2 cho bảng 2x2 cho phép xác nhận/phủ nhận mối liên hệ này.

Tuy nhiên trong trường hợp này ta có đến 4 biến A,B,C,D và mỗi biến có 2 bậc (Y và N), do đó nếu sử dụng bảng chéo, kích thước của bảng là rất lớn (4 chiều): 2x2x2x2

Trong R, ta có thể dựng bảng chéo bằng 2 cách: hoặc dùng hàm xtabs của package vcd

library(vcd)
## Loading required package: grid
xtabs(~BenhA+BenhB+BenhC+BenhD,data=dat)%>%knitr::kable()
BenhA BenhB BenhC BenhD Freq
N N N N 45
Y N N N 33
N Y N N 53
Y Y N N 22
N N Y N 11
Y N Y N 5
N Y Y N 18
Y Y Y N 7
N N N Y 76
Y N N Y 29
N Y N Y 97
Y Y N Y 42
N N Y Y 24
Y N Y Y 10
N Y Y Y 19
Y Y Y Y 9

Hoặc dùng hàm tally( ) của package dplyr (Nhi khuyến khích các bạn dùng cách này)

dat%>%
  group_by(BenhA,BenhB,BenhC,BenhD)%>%
  tally()%>%
  knitr::kable()
BenhA BenhB BenhC BenhD n
N N N N 45
N N N Y 76
N N Y N 11
N N Y Y 24
N Y N N 53
N Y N Y 97
N Y Y N 18
N Y Y Y 19
Y N N N 33
Y N N Y 29
Y N Y N 5
Y N Y Y 10
Y Y N N 22
Y Y N Y 42
Y Y Y N 7
Y Y Y Y 9

Hoặc phổ quát hơn là hàm summary cũng của dplyr với phép đếm tần suất

dat%>%
  group_by(BenhA,BenhB,BenhC,BenhD)%>%
  summarise(frequency = n())%>%
    knitr::kable()
BenhA BenhB BenhC BenhD frequency
N N N N 45
N N N Y 76
N N Y N 11
N N Y Y 24
N Y N N 53
N Y N Y 97
N Y Y N 18
N Y Y Y 19
Y N N N 33
Y N N Y 29
Y N Y N 5
Y N Y Y 10
Y Y N N 22
Y Y N Y 42
Y Y Y N 7
Y Y Y Y 9

2 Giới hạn của biểu đồ 1 chiều

Trước hết, cần phân biệt giữa 2 khái niệm: Mô tả 4 biến định tính và phân tích tương quan giữa chúng. Nếu mục tiêu của ta chỉ đơn giản là trình bày 4 biến (Benh A,B,C,D) thì đây là một điều rất dễ dàng:

Đầu tiên, ta có thể dùng heatmap, vì nó cho phép mô tả toàn bộ bảng dữ liệu (heatmap thực chất chỉ là 1 matrix được tô màu). Heatmap có thể dùng được cho cả biến định lượng lẫn định tính:

dat2<-apply(dat,2,as.factor)%>%as.data.frame()
dat2[,c(2:5)]<-map(dat2[,c(2:5)],as.numeric)
dat2[,c(2:5)]<-dat2[,c(2:5)]-1

dat2%>%gather(BenhA:BenhD,key="Benh",value="value")%>%
  ggplot(aes(x=reorder(Benhnhan,1-value),y=reorder(Benh,value),fill=value))+
  geom_tile()+
  theme_bw()+scale_fill_gradient(low="blue",high="red")+
  scale_x_discrete("Benh nhan",labels = NULL)+
  scale_y_discrete("Benh ly")

Hoặc ta có thể dùng 4 biểu đồ 1 chiều, thí dụ 4 biểu đồ bánh, 4 biểu đồ thanh …

Biểu đồ thanh là phát minh của William Playfair (1759-1823) vào năm 1801, sử dụng thành phần hình họa là hộp chữ nhật. Một cạnh trên hộp chữ nhật này (chiều cao hoặc chiều dài) được chọn để biểu hiện cho một giá trị định lượng, trong khi trung điểm cạnh còn lại định vị cho phân nhóm. Ưu điểm lớn nhất của biểu đồ thanh là tính tiện dụng. Đây là dạng biểu đồ phổ biến và đa dụng nhất, đến mức nó trở thành một trong 3 biểu tượng của ngành thống kê (bên cạnh hình chuông úp và biểu đồ bánh).

Biểu đồ thanh được ví như một con dao xếp Thụy Sỹ vì bạn có thể dùng nó để trình bày bất cứ điều gì, trong mọi hoàn cảnh và có khả năng thay thế cho mọi loại biểu đồ khác. Sự rời rạc của biểu đồ thanh khiến nó phù hợp để trình bày tần suất hoặc tỉ lệ của biến định tính/rời rạc.

Một biến thể của biểu đồ thanh được tạo ra bằng sự phân khúc. Biểu đồ thanh phân khúc cho phép trình bày 1 biến định lượng Y (thí dụ tần suất) trên trục tung phụ thuộc vào 1 biến rời rạc X1 trên trục hoành và được điều kiện hóa theo 1 yếu tố phân nhóm X2 khác. Như vậy biểu đồ này cho phép biểu diễn đồng thời 3 chiều thông tin.

Khi trục tung biểu diễn tỉ lệ %, kích thước của các thanh trên biều đồ được chuẩn hóa (bằng nhau), ta sẽ có một dạng biểu đồ đặc biệt - có công dụng trình bày tỉ lệ phân bố (%) các nhãn giá trị của một biến rời rạc. Dữ liệu được một hàm thống kê hoán chuyển thành tỉ lệ % (tần suất hiện diện/tổng số trường hợp), sau đó tỉ lệ mỗi nhãn giá trị (phân nhóm) được biểu thị bởi kích thước 1 cạnh hình hộp chữ nhật. Các hộp chữ nhật này được xếp nối tiếp nhau và được phân biệt bằng màu nền (một phổ màu rời rạc). Do cạnh còn lại của những hộp chữ nhật nhỏ này là đồng kích thước, nên hiệu ứng tương phản hoàn toàn do diện tích của mỗi hình hộp (Kết quả (%) tỉ lệ thuận với diện tích của mỗi hình hộp).

Từ biểu đồ thanh phân khúc, ta có thể tạo ra biểu đồ bánh hoặc Cox-Comb (hoán chuyển hệ trục tọa độ Descartes thành Polar).

Nếu dùng biểu đồ thanh ta sẽ có hình ảnh như sau:

dat%>%gather(BenhA:BenhD,value="Chandoan",key="Benh")%>%
  ggplot(aes(x=Benh,fill=Chandoan))+
  geom_bar(stat = "count",position="dodge",show.legend = T,alpha=0.7,col="black")+
  theme_bw()+
  scale_fill_manual(values=c("blue","red"))

Hoặc như thế này:

dat%>%gather(BenhA:BenhD,value="Chandoan",key="Benh")%>%
  ggplot(aes(x=Benh,fill=Chandoan))+
  geom_bar(stat = "count",position="fill",show.legend = T,alpha=0.7,col="black")+
  theme_bw()+
  scale_fill_manual(values=c("blue","red"))

Còn đây là kết quả của 4 biểu đồ bánh:

blank_theme <- theme_minimal()+
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    panel.border = element_blank(),
    panel.grid=element_blank(),
    axis.ticks = element_blank(),
    plot.title=element_text(size=14, face="bold")
  )
dat%>%gather(BenhA:BenhD,value="Chandoan",key="Benh")%>%
  ggplot(aes(x="",fill=Chandoan))+
  geom_bar(width = 1,stat = "count",show.legend = T,alpha=0.7,col="black")+
  coord_polar(theta="y")+
  scale_y_discrete(breaks=NULL,labels = NULL)+
  blank_theme+
    facet_wrap(~Benh,ncol=4)+
  scale_fill_manual(values=c("blue","red"))

Tuy nhiên, những biểu đồ kể trên chỉ mới cho phép chúng ta mô tả và so sánh, hoặc tối đa là bảng chéo 2 chiều, nhưng chưa thực sự thay thế được cho bảng chéo 3-4 chiều, vì chúng không trình bày được mối liên hệ giữa 4 bệnh lý. Mục tiêu xa hơn của chúng ta là chuyển bảng chéo 2x2x2x2 thành biểu đồ. Có thể làm điều này nhờ vào 6 nhóm công cụ như sau:

3 Hàm facet grid:

Khi kết hợp giữa biểu đồ thanh và 1 hàm facet_grid() – điều kiện hóa theo 2 biến – ta có thể trình bày được một bảng chéo tối đa là 4 chiều, thí dụ 2x2x2x2 như trong trường hợp này.

dat%>%ggplot(aes(fill=BenhB,
                x=BenhA))+
  geom_bar(stat="count",
           position="fill",
           color="black",
           alpha=0.7,
           show.legend = T)+
  theme_bw()+
  facet_grid(BenhC~BenhD)+
  scale_fill_manual(values = c("blue","red"))

dat%>%ggplot(aes(fill=BenhB,
                x=BenhA))+
  geom_bar(stat="count",
           position="dodge",
           color="black",
           alpha=0.7,
           show.legend = T)+
  theme_bw()+
  facet_grid(BenhC~BenhD)+
  scale_fill_manual(values = c("blue","red"))

4 Biểu đồ khảm (Mosaic plot)

Khảm (mosaic) là một phương pháp trang trí trong mỹ thuật và kiến trúc, với cơ chế sắp xếp những mảnh ghép bằng vật liệu đá, gốm sứ hoặc thủy tinh có hình thù và màu sắc khác nhau để lấp đầy một mặt phẳng (cửa sổ, nền nhà, tường, tranh…). Biểu đồ mosaic áp dụng cơ chế tương tự để hình ảnh hóa một bảng tương quan tần số giữa nhiều biến định tính.

Biểu đồ Mosaic được thiết lập một cách chính thức bởi 2 tác giả Hartigan và Kleiner vào năm 1981, tuy nhiên theo tác giả Micheal Friendly (2011), biểu đồ này có nguồn gốc cổ xưa hơn rất nhiều. Cụ thể là từ năm 1693, Edmund Halley đã có ý tưởng trình bày dữ liệu dưới hình thức chiều cao x chiều rộng của hình chữ nhật (giản đồ Halley). Sau đó, ý tưởng dùng diện tích hình phẳng để trình bày dữ liệu cũng được nhiều tác giả khác sử dụng như Charles de Fourcroy (Pháp, 1780), August Friedrich Wilhem Crome (Đức, 1785). Với sự xuất hiện của nhiều biểu đồ thống kê cơ bản, trong đó có biểu đồ thanh vào đầu thế kỉ 19, có thể xem biểu đồ Mosaic là sự biến tấu từ biểu đồ thanh và biểu đồ thanh phân khúc bằng cách tận dụng cả 2 chiều hình hộp và diện tích để biểu diễn thông tin. Thí dụ Jacques bertillon (Pháp, 1896) đã dùng hình hộp chồng lên bản đồ, Charles Joseph Minard (1844) xếp các biểu đồ thanh phân khúc sát với nhau thành một bảng chéo. Biểu đồ Mosaic sơ khai nhất có lẽ là sản phẩm của Georg von Mayr (1877), nó có nội dung là một bảng chéo 3x3 cho 2 biến, với cùng nguyên tắc như mosaic plot mà ta biết ngày nay.

Biểu đồ mosaic plot bao gồm một diện tích hình vuông hoặc chữ nhật (không gian 2 chiều) được chia thành nhiều mảnh hình hộp chữ nhật trong đó kích thước một cạnh (thí dụ chiều rộng) tương ứng với tần suất hay tỉ lệ mỗi bậc giá trị của 1 biến X1. Mỗi mảnh hình hộp này sau đó lại được chia thành các mảnh nhỏ hơn (tile) mà kích thước (chiều cao) mỗi mảnh thể hiện tần suất/tỉ lệ cho một biến thứ 2 (X2). Quy trình phân chia này có thể tiếp diễn cho nhiều biến khác nữa … Ở từng cấp độ phân chia, diện tích của mỗi mảnh ghép (tile) tỉ lệ với giá trị mỗi ô trong một bảng chéo (bảng phân phối tần số) tổ hợp giữa 2-3 biến được khảo sát. Như vậy có thể nói rằng biểu đồ mosaic chính là hình ảnh của một bảng chéo khảo sát mối liên hệ/tương tác giữa 2 hay nhiều biến định tính. Nếu các biến này độc lập với nhau (không có mối liên hệ), sẽ không có sự khác biệt đáng kể về tần suất giữa các ô và các mảnh ghép hình chữ nhật sẽ song song và đồng dạng; trái lại khi có mối liên hệ giữa 2-3 biến trong bảng chéo, các mảnh ghép sẽ có diện tích to nhỏ khác nhau, tạo nên hình ảnh lệch so le…

Có thể vẽ mosaic plot rất dễ dàng từ một bảng chéo nhờ package vcd:

xtb2<-xtabs(~BenhA+BenhB+BenhC+BenhD,data=dat)

vcd::assoc(xtb2, shade=TRUE)

vcd::mosaic(xtb2,shade=T)

Một cách tổng quát, cơ chế của biểu đồ Mosaic là một hàm với nội dung: Weight ~ tập biến Xj, với Weight là trọng số diện tích. Trường hợp đơn giản nhất, ta chỉ có 1 biến: Weight ~ X, biểu đồ Mosaic chính là một biểu đồ thanh phân khúc (stacked bar graph).

Package ggmosaic cho phép tùy chỉnh nội dung của hàm này và tích hợp biểu đồ Mosaic vào ngữ pháp ggplot2 qua hàm geom_mosaic( ).

library(ggmosaic)

dat%>%ggplot()+geom_mosaic(aes(weight = 1, 
                              x = product(BenhB,BenhC), 
                              fill=BenhA),
                          col="black",na.rm=TRUE,
                          offset = 0.01)+
    scale_fill_manual(values = c("blue","red"))+
  theme_bw()+facet_grid(BenhD~.)+coord_flip()

library(ggmosaic)

dat%>%ggplot()+geom_mosaic(aes(weight = 1, 
                              x = product(BenhB,BenhC,BenhD), 
                              fill=BenhA),
                          col="black",na.rm=TRUE,
                          offset = 0.01)+
    scale_fill_manual(values = c("blue","red"))+
  theme_bw()

5 Biểu đồ lưu lượng (Flow diagram)

Nếu ta hình dung mỗi ô trong bảng chéo là một nhánh sông/suối hay ngăn chứa trong một cái hồ, còn tập hợp 500 cá thể bệnh nhân như một khối chất lỏng (nước), thì sự phân bố tần suất vào các ô trong bảng chéo cũng giống như rót nước vào từng ngăn hoặc một dòng chảy của nước qua mạng lưới sông/suối này. Có một dạng đồ họa đặc biệt cho phép mô tả dòng chảy này, đó là các biểu đồ lưu lượng (flow diagram). Ý tưởng về flow diagram do Charles Minard phát minh vào năm 1869 (biểu đồ hành quân của Napoleon vào nước Nga).

Flow diagram được biết dưới nhiều cái tên khác nhau như biểu đồ Sankey (từ tên của tác giả Matthew HP. Sankey đã dùng biểu đồ này vào năm 1898 để mô tả dòng năng lượng qua một động cơ hơi nước), hoặc Alluvial diagram, là một dạng flow diagram đặc biệt mô tả sự thay đổi cấu trúc của một hệ thống (network) theo thời gian (thay vì không gian như Sankey plot).

Flow diagram được sử dụng nhiều trong ngành Vật lý, Địa lý, Thủy lợi, Công/Nông nghiệp và kinh tế. Tuy nhiên ý tưởng của nó có thể áp dụng tổng quát cho mọi vấn đề liên quan đến phân bố tần suất của biến định tính/phân nhóm.

Ta có thể vẽ biểu đồ Sankey hay alluvial bằng package ggalluvial trong R:

library(ggalluvial)
library(pals)

xtb=dat%>%
  dplyr::group_by(BenhA,BenhB,BenhC,BenhD)%>%
  summarise(frequency = n())

xtb%>%ggplot(aes(weight = frequency, 
                  axis1 = BenhA, 
                 axis2= BenhB,
                 axis3=BenhC,
                axis4 = BenhD)) +
  geom_alluvium(aes(fill = frequency), width = 0.3) +
  geom_stratum(width = 1/10, 
               fill = "white", 
               color = "grey") +
  geom_label(stat = "stratum", 
             label.strata = TRUE) +
  scale_x_continuous(breaks = 1:4, 
                     labels = c("A", "B","C","D"))+
  scale_fill_gradientn(colours = pals::parula(n=500))+
  theme_bw()+theme(legend.position="bottom")

xtb%>%ggplot(aes(weight = frequency, 
                 axis1 = BenhA, 
                 axis2= BenhB,
                 axis3=BenhC,
                axis4 = BenhD))+
  geom_alluvium(aes(fill =BenhA),
                width = 0, 
                reverse = FALSE) +
  guides(fill = FALSE)+
  geom_stratum(width = 1/10, 
               reverse = FALSE)+
  geom_text(stat = "stratum", 
            label.strata = TRUE, 
            reverse = FALSE) +
  scale_x_continuous(breaks = 1:4, 
                     labels = c("BenhA","BenhB", "BenhC", "BenhD"))+
  coord_flip()+theme_bw()

6 Biểu đồ dây cung

Đây là một phát kiến mới trong ngành đồ họa thống kê (nó được áp dụng lần đầu tiên vào năm 2007 để minh họa về mạng lưới quan hệ phức tạp giữa các gene trong bộ gene.

Ý tưởng của chord diagram phát xuất từ khái niệm “dây cung” trên đường tròn trong môn hình học phẳng (một đoạn thẳng có 2 đầu đều nằm trên đường tròn). Bản chất của chord diagram là một biểu đồ mạng (network) được sắp xếp theo hình tròn, do đó nó có thể được xếp vào họ biểu đồ hình tròn (circular layout graph). Cơ chế của chord diagram như sau: Dữ liệu được sắp xếp trên đường tròn thành từng cung (phân nhóm) hoặc điểm (cá thể), sau đó liên hệ giữa các phân nhóm/cá thể này được diễn tả bằng những dây cung.

Về ý nghĩa truyền thông, chord diagram tốt cả về lượng lẫn về phẩm: nó cho phép tải số lượng thông tin rất lớn (vô số nhóm/cá thể) có thể được xếp nối tiếp trên cung tròn). Chord diagram trình bày được cả thông tin về cấu trúc và sự tương phản. Diện tích (độ dầy) của mỗi dây còn cho biết tần suất của tổ hợp cũng như độ mạnh của mối liên kết. Ngoài ra, chord diagram còn được ưa chuộng vì tính mỹ thuật: màu sắc và sự chồng lắp của nhiều dây cung mang lại một hiệu ứng đẹp quyến rũ.

Các bạn có thể tham khảo quyển sách “Circular Visualisation in R” của Zuguang Gu để biết thêm về họ biểu đồ trên cung tròn http://zuguang.de/circlize_book/book/

library(circlize)

xtb3=dat%>%mutate(Id=rownames(dat))%>%gather(BenhA:BenhD,key="Benh",value="Status")%>%
  group_by(Status,Benh)%>%
  summarise(frequency = n())

grid.col = c(`Y` ="red",`N` ="blue",
              `BenhA` ="orange",`BenhB` ="green",
               `BenhC` ="purple",`BenhD` ="pink")

chordDiagram(as.data.frame(xtb3), 
             transparency = 0.5,grid.col =grid.col)

7 Sơ đồ Venn

Giản đồ Venn hay còn gọi là sơ đồ tập hợp, là một hình thức minh họa cho các liên hệ mang tính logic giữa 2 hay nhiều tập hợp. Nó được sử dụng rất phổ biến trong môn logic học, lý thuyết xác suất, thống kê và cả trong truyền thông đại chúng. Sơ đồ Venn được phát minh vào năm 1880 bởi John Venn.

Trong sơ đồ Venn, mỗi tập hợp được đại diện bằng một hình tròn hay Elip có màu sắc riêng biệt với diện tích tỉ lệ với số phần tử mà nó chứa. Phần chung giữa 2 hay nhiều tập hợp chính là kết quả của việc chồng lắp một phần những hình tròn/elip này lên nhau.

Trong R có hai cách để vẽ sơ đồ Venn, cách làm cũ dựa vào package limma từ bioconductor:

# source("http://bioconductor.org/biocLite.R")

# biocLite("limma")

# library(limma)

# dat2[,c(2:5)]%>%vennCounts()%>%vennDiagram(names = c("A","B","C","D"),circle.col = c("#fc113c","#11a6fc","#11fc6b","#fcb211"))

Một phương pháp khác mới được tạo ra gần đây (tháng 5/2018), đó là package nVennR.

library(nVennR)

vplot<-list(A=filter(dat,BenhA=="Y")%>%.$Benhnhan,
           B=filter(dat,BenhB=="Y")%>%.$Benhnhan,
           C=filter(dat,BenhC=="Y")%>%.$Benhnhan,
           D=filter(dat,BenhD=="Y")%>%.$Benhnhan
)%>%plotVenn()

showSVG(nVennObj = vplot, 
        setColors = c("#2cabf9","#2cf9bc","#93f92c","#f9ca2c"),
        borderWidth = 3,opacity = 0.2,
        labelRegions = F, fontScale = 3)

8 Luật kết hợp - biểu đồ mạng

Ngoài những biểu đồ thuộc lớp “function” kể trên, ta còn có thể sử dụng phương pháp luật kết hợp (association rule) và tạo ra 1 biểu đồ mạng (network) như sau:

library(arules)
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## The following object is masked from 'package:tidyr':
## 
##     expand
## 
## Attaching package: 'arules'
## The following object is masked from 'package:dplyr':
## 
##     recode
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
library(arulesViz)

rules=apriori(dat[,-1], control = list(verbose=F),
              parameter = list(minlen=2, 
                               supp=10/500, 
                               conf=0.8))

rules <- sort(rules, by="lift")

redundant=is.redundant(rules,measure="confidence")

rule2=rules[!redundant]

plot(rule2, method="graph",engine="htmlwidget")

9 Kết luận

Bài thực hành đến đây là hết, các bạn đã biết cách sử dụng 6 công cụ khác nhau để chuyển bảng chéo đa chiều thành biểu đồ. Tùy bản chất vấn đề và phong cách cá nhân, yêu cầu mỹ thuật, bạn có thể dùng một trong những công cụ này cho luận văn/bài thuyết trình/văn bản khoa học của mình để thay thế cho những bảng chéo rối rắm.

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

LS0tDQp0aXRsZTogIsSQ4buTIGjhu41hIMOhcCBk4bulbmcgY2hvIGLhuqNuZyBjaMOpbyIgDQphdXRob3I6ICJMw6ogTmfhu41jIEto4bqjIE5oaSINCmRhdGU6ICIxNyBUaMOhbmcgNSAyMDE4Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogImRlZmF1bHQiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQohW10oY2F0Z3JhcGgucG5nKQ0KDQojIEdp4bubaSB0aGnhu4d1DQoNClBow6JuIHTDrWNoIHRy4buxYyBxdWFuIGLhurFuZyDEkeG7kyBo4buNYSB0aOG7kW5nIGvDqiBsw6AgbeG7mXQgZ2nhuqNpIHBow6FwIG3DtCB04bqjIGThu68gbGnhu4d1IHbhu5tpIGhp4buHdSBxdeG6oyBjYW8gaMahbiBo4bqzbiBzbyB24bubaSBow6xuaCB0aOG7qWMgZMO5bmcgYuG6o25nIHbDoCBjb24gc+G7kS4gVHV5IG5oacOqbiwgxJFhIHPhu5EgZ2nDoW8gdHLDrG5oL3TDoGkgbGnhu4d1IHRyb25nIHRyxrDhu51uZyBs4bubcCBoaeG7h24gbmF5IGPDsm4ga2hp4bq/bSBraHV54bq/dCBy4bqldCBuaGnhu4F1IHbhu4EgbeG6o25nIGJp4bq/biDEkeG7i25oIHTDrW5oL3Lhu51pIHLhuqFjIChjYXRlZ29yaWNhbC9kaXNjcmV0ZSB2YXJpYWJsZXMpLiBUaMOtIGThu6UsIGNobyBt4bulYyB0acOqdSBtw7QgdOG6oyBob+G6t2Mga2jhuqNvIHPDoXQgbeG7kWkgdMawxqFuZyBxdWFuIGdp4buvYSBjw6FjIGJp4bq/biDEkeG7i25oIHTDrW5oIDogY8OhYyBjw7RuZyBj4bulIHBo4buVIHRow7RuZyBt4bubaSBjaOG7iSBjaG8gcGjDqXAgdHLDrG5oIGLDoHkgdGjDtG5nIHRpbiAxIGNoaeG7gXUgKGJp4buDdSDEkeG7kyBiw6FuaCkgaG/hurdjIDIgY2hp4buBdSAoYmnhu4N1IMSR4buTIGjhu5lwKS4gDQoNClRyb25nIG7Eg20gMjAxOCBuw6B5LCBOaGkgc+G6vSBwaMOhdCBow6BuaCBt4buZdCBxdXnhu4NuIHPDoWNoIGNodXnDqm4gxJHhu4EgduG7gSDCqyDEkOG7kyBo4buNYSB0aOG7kW5nIGvDqiDCuywgdHJvbmcgxJHDsyBz4bq9IGdp4bubaSB0aGnhu4d1IHbhu4EgdOG6pXQgY+G6oyBuaOG7r25nIGThuqFuZyBiaeG7g3UgxJHhu5MgdGjhu5FuZyBrw6ogY2hvIDMg4bupbmcgZOG7pW5nIDogbcO0IHThuqMgxJHhurdjIHTDrW5oIHBow6JuIGLhu5EsIHNvIHPDoW5oIHRy4buxYyBxdWFuLCBwaMOibiB0w61jaCB0xrDGoW5nIHF1YW4sIGNobyBj4bqjIGJp4bq/biDEkeG7i25oIGzGsOG7o25nIHbDoCDEkeG7i25oIHTDrW5oLg0KDQohW10oYmlhc2FjaC5wbmcpDQoNClRyb25nIGLDoGkgdGjhu7FjIGjDoG5oIGjDtG0gbmF5LCBOaGkgc+G6vSB0csOtY2ggbeG7mXQgcGjhuqduIG5o4buPIG7DtGkgZHVuZyBjaMawxqFuZyAzIGPDsyBjaOG7pyDEkeG7gSA6IMKrIHBow6JuIHTDrWNoIHF1YW4gaOG7hyBnaeG7r2Egbmhp4buBdSBiaeG6v24gxJHhu4tuaCB0w61uaCDCuyDEkeG7gyBnaeG7m2kgdGhp4buHdSB24bubaSBjw6FjIGLhuqFuIDYgY8O0bmcgY+G7pSDEkeG7kyBo4buNYSBjaHV5w6puIGThu6VuZyBn4buTbSA6DQoNCjEpCUPDoWMgYmnhur9uIHRo4buDIGPhu6dhIGJp4buDdSDEkeG7kyB0aGFuaCAoQmFyIGdyYXBoKQ0KDQoyKQlCaeG7g3UgxJHhu5Mga2jhuqNtIChNb3NhaWMgcGxvdCkNCg0KMykJQmnhu4N1IMSR4buTIGzGsHUgbMaw4bujbmcgKEZsb3cgZGlhZ3JhbSkNCg0KNCkJQmnhu4N1IMSR4buTIGTDonkgY3VuZyAoQ2hvcmQgZGlhZ3JhbSkNCg0KNSkJU8ahIMSR4buTIFZlbm4NCg0KNikgQmnhu4N1IMSR4buTIG3huqFuZyB04burIGx14bqtdCBr4bq/dCBo4bujcA0KDQpOaOG7r25nIGPDtG5nIGPhu6UgbsOgeSBjw7MgdGjhu4MgxJHGsOG7o2MgZMO5bmcgxJHhu4MgbcO0IHThuqMgdHLhu7FjIHF1YW4gYuG6o25nIGNow6lvIMSRYSBjaGnhu4F1IMSR4buDIGto4bqjbyBzw6F0IHF1YW4gaOG7hyB04burIDMgYmnhur9uIMSR4buLbmggdMOtbmggdHLhu58gbMOqbg0KDQpUcsaw4bubYyBo4bq/dCwgdGEgc+G6vSB04bqhbyAxIGThu68gbGnhu4d1IG3DtCBwaOG7j25nLCBjw7MgbuG7mWkgZHVuZyBraOG6o28gc8OhdCB04bqnbiBzdeG6pXQgY+G7p2EgNCBi4buHbmggbMO9IGtow6FjIG5oYXUgKEEsQixDLEQpIHRyb25nIG3hu5l0IG3huqt1IDUwMCBi4buHbmggbmjDom4uIEhp4buHbiB0xrDhu6NuZyBuaGnhu4F1IGLhu4duaCBjw7luZyBoaeG7h24gZGnhu4duIHRyw6puIG3hu5l0IGPDoSB0aOG7gyDEkcaw4bujYyBn4buNaSBsw6AgY29tb3JiaWRpdHkgdHJvbmcgeSBo4buNYyBsw6JtIHPDoG5nICh0aMOtIGThu6UgbeG7mXQgbmfGsOG7nWkgxJHDoG4gw7RuZyBjw7MgdGjhu4MgbeG6r2MgYuG7h25oIENPUEQga+G6v3QgaOG7o3AgduG7m2kgU3V5IHRpbSB2w6AgVGnhu4N1IMSRxrDhu51uZykuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCnNldC5zZWVkKDE3MDUpDQoNCmRhdD1kYXRhX2ZyYW1lKEJlbmhuaGFuPXBhc3RlKCJCTiIsYygxOjUwMCksc2VwPSIiKSwNCiAgICAgICAgICAgICAgIEJlbmhBPXNhbXBsZShjKCJZIiwiTiIpLDUwMCwgcmVwbGFjZT1ULHByb2I9YygwLjMsMC43KSklPiVhcy5mYWN0b3IoKSwNCiAgICAgICAgICAgICAgIEJlbmhCPXNhbXBsZShjKCJZIiwiTiIpLDUwMCwgcmVwbGFjZT1ULHByb2I9YygwLjU1LDAuNDUpKSU+JWFzLmZhY3RvcigpLA0KICAgICAgICAgICAgICAgQmVuaEM9c2FtcGxlKGMoIlkiLCJOIiksNTAwLCByZXBsYWNlPVQscHJvYj1jKDAuMiwwLjgpKSU+JWFzLmZhY3RvcigpLA0KICAgICAgICAgICAgICAgQmVuaEQ9c2FtcGxlKGMoIlkiLCJOIiksNTAwLCByZXBsYWNlPVQscHJvYj1jKDAuNiwwLjQpKSU+JWFzLmZhY3RvcigpDQopDQoNCmhlYWQoZGF0KSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KVGjhu5FuZyBrw6ogdGjGsOG7nW5nIHF1eSB0aMaw4budbmcgc+G7rSBk4bulbmcgbeG7mXQgYuG6o25nIGNow6lvIMSR4buDIG3DtCB04bqjIHPhu7EgcGjDom4gYuG7kSB04bqnbiBzdeG6pXQvdOG7iSBs4buHIGPhu6dhIHThuqV0IGPhuqMgY8OhYyB04buVIGjhu6NwIGPDsyB0aOG7gyBnaeG7r2EgMiBoYXkgbmhp4buBdSBiaeG6v24gxJHhu4tuaCB0w61uaC5LaGkgY8OzIHPhu7EgY2hpYSBz4bq7IGNodW5nIG3hu5l0IHThu4kgbOG7hyBjYW8gY+G7p2EgbeG7mXQgYuG6rWMgKHRy4bqhbmcgdGjDoWkpIGdp4buvYSAyIGJp4bq/biwgdGEgbsOzaSAyIGJp4bq/biBuw6B5IGPDsyBz4buxIGxpw6puIGjhu4cgduG7m2kgbmhhdSwgdGjDrSBk4bulIG3hu5l0IHRyaeG7h3UgY2jhu6luZyBYIHh14bqldCBoaeG7h24gKFg9WWVzKSBwaOG7lSBiaeG6v24g4bufIGPDoWMgYuG7h25oIG5ow6JuIChEPVllcykuIE3hu5l0IGtp4buDbSDEkeG7i25oIENoaS0yIGNobyBi4bqjbmcgMngyIGNobyBwaMOpcCB4w6FjIG5o4bqtbi9waOG7pyBuaOG6rW4gbeG7kWkgbGnDqm4gaOG7hyBuw6B5Lg0KDQpUdXkgbmhpw6puIHRyb25nIHRyxrDhu51uZyBo4bujcCBuw6B5IHRhIGPDsyDEkeG6v24gNCBiaeG6v24gQSxCLEMsRCB2w6AgbeG7l2kgYmnhur9uIGPDsyAyIGLhuq1jIChZIHbDoCBOKSwgZG8gxJHDsyBu4bq/dSBz4butIGThu6VuZyBi4bqjbmcgY2jDqW8sIGvDrWNoIHRoxrDhu5tjIGPhu6dhIGLhuqNuZyBsw6AgcuG6pXQgbOG7m24gKDQgY2hp4buBdSk6IDJ4MngyeDINCg0KVHJvbmcgUiwgdGEgY8OzIHRo4buDIGThu7FuZyBi4bqjbmcgY2jDqW8gYuG6sW5nIDIgY8OhY2g6IGhv4bq3YyBkw7luZyBow6BtIHh0YWJzIGPhu6dhIHBhY2thZ2UgdmNkDQoNCmBgYHtyfQ0KbGlicmFyeSh2Y2QpDQoNCnh0YWJzKH5CZW5oQStCZW5oQitCZW5oQytCZW5oRCxkYXRhPWRhdCklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNCkhv4bq3YyBkw7luZyBow6BtIHRhbGx5KCApIGPhu6dhIHBhY2thZ2UgZHBseXIgKE5oaSBraHV54bq/biBraMOtY2ggY8OhYyBi4bqhbiBkw7luZyBjw6FjaCBuw6B5KQ0KDQpgYGB7cn0NCmRhdCU+JQ0KICBncm91cF9ieShCZW5oQSxCZW5oQixCZW5oQyxCZW5oRCklPiUNCiAgdGFsbHkoKSU+JQ0KICBrbml0cjo6a2FibGUoKQ0KYGBgDQoNCkhv4bq3YyBwaOG7lSBxdcOhdCBoxqFuIGzDoCBow6BtIHN1bW1hcnkgY8WpbmcgY+G7p2EgZHBseXIgduG7m2kgcGjDqXAgxJHhur9tIHThuqduIHN14bqldA0KDQpgYGB7cn0NCmRhdCU+JQ0KICBncm91cF9ieShCZW5oQSxCZW5oQixCZW5oQyxCZW5oRCklPiUNCiAgc3VtbWFyaXNlKGZyZXF1ZW5jeSA9IG4oKSklPiUNCiAgICBrbml0cjo6a2FibGUoKQ0KYGBgDQoNCiMgR2nhu5tpIGjhuqFuIGPhu6dhIGJp4buDdSDEkeG7kyAxIGNoaeG7gXUNCg0KVHLGsOG7m2MgaOG6v3QsIGPhuqduIHBow6JuIGJp4buHdCBnaeG7r2EgMiBraMOhaSBuaeG7h206IE3DtCB04bqjIDQgYmnhur9uIMSR4buLbmggdMOtbmggdsOgIHBow6JuIHTDrWNoIHTGsMahbmcgcXVhbiBnaeG7r2EgY2jDum5nLiBO4bq/dSBt4bulYyB0acOqdSBj4bunYSB0YSBjaOG7iSDEkcahbiBnaeG6o24gbMOgIHRyw6xuaCBiw6B5IDQgYmnhur9uIChCZW5oIEEsQixDLEQpIHRow6wgxJHDonkgbMOgIG3hu5l0IMSRaeG7gXUgcuG6pXQgZOG7hSBkw6BuZzoNCg0KxJDhuqd1IHRpw6puLCB0YSBjw7MgdGjhu4MgZMO5bmcgaGVhdG1hcCwgdsOsIG7DsyBjaG8gcGjDqXAgbcO0IHThuqMgdG/DoG4gYuG7mSBi4bqjbmcgZOG7ryBsaeG7h3UgKGhlYXRtYXAgdGjhu7FjIGNo4bqldCBjaOG7iSBsw6AgMSBtYXRyaXggxJHGsOG7o2MgdMO0IG3DoHUpLiBIZWF0bWFwIGPDsyB0aOG7gyBkw7luZyDEkcaw4bujYyBjaG8gY+G6oyBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyBs4bqrbiDEkeG7i25oIHTDrW5oOg0KDQpgYGB7cn0NCmRhdDI8LWFwcGx5KGRhdCwyLGFzLmZhY3RvciklPiVhcy5kYXRhLmZyYW1lKCkNCmRhdDJbLGMoMjo1KV08LW1hcChkYXQyWyxjKDI6NSldLGFzLm51bWVyaWMpDQpkYXQyWyxjKDI6NSldPC1kYXQyWyxjKDI6NSldLTENCg0KZGF0MiU+JWdhdGhlcihCZW5oQTpCZW5oRCxrZXk9IkJlbmgiLHZhbHVlPSJ2YWx1ZSIpJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKEJlbmhuaGFuLDEtdmFsdWUpLHk9cmVvcmRlcihCZW5oLHZhbHVlKSxmaWxsPXZhbHVlKSkrDQogIGdlb21fdGlsZSgpKw0KICB0aGVtZV9idygpK3NjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSJibHVlIixoaWdoPSJyZWQiKSsNCiAgc2NhbGVfeF9kaXNjcmV0ZSgiQmVuaCBuaGFuIixsYWJlbHMgPSBOVUxMKSsNCiAgc2NhbGVfeV9kaXNjcmV0ZSgiQmVuaCBseSIpDQpgYGANCg0KSG/hurdjIHRhIGPDsyB0aOG7gyBkw7luZyA0IGJp4buDdSDEkeG7kyAxIGNoaeG7gXUsIHRow60gZOG7pSA0IGJp4buDdSDEkeG7kyBiw6FuaCwgNCBiaeG7g3UgxJHhu5MgdGhhbmggLi4uDQoNCkJp4buDdSDEkeG7kyB0aGFuaCBsw6AgcGjDoXQgbWluaCBj4bunYSBXaWxsaWFtIFBsYXlmYWlyICgxNzU5LTE4MjMpIHbDoG8gbsSDbSAxODAxLCBz4butIGThu6VuZyB0aMOgbmggcGjhuqduIGjDrG5oIGjhu41hIGzDoCBo4buZcCBjaOG7ryBuaOG6rXQuIE3hu5l0IGPhuqFuaCB0csOqbiBo4buZcCBjaOG7ryBuaOG6rXQgbsOgeSAoY2hp4buBdSBjYW8gaG/hurdjIGNoaeG7gXUgZMOgaSkgxJHGsOG7o2MgY2jhu41uIMSR4buDIGJp4buDdSBoaeG7h24gY2hvIG3hu5l0IGdpw6EgdHLhu4sgxJHhu4tuaCBsxrDhu6NuZywgdHJvbmcga2hpIHRydW5nIMSRaeG7g20gY+G6oW5oIGPDsm4gbOG6oWkgxJHhu4tuaCB24buLIGNobyBwaMOibiBuaMOzbS4gxq91IMSRaeG7g20gbOG7m24gbmjhuqV0IGPhu6dhIGJp4buDdSDEkeG7kyB0aGFuaCBsw6AgdMOtbmggdGnhu4duIGThu6VuZy4gxJDDonkgbMOgIGThuqFuZyBiaeG7g3UgxJHhu5MgcGjhu5UgYmnhur9uIHbDoCDEkWEgZOG7pW5nIG5o4bqldCwgxJHhur9uIG3hu6ljIG7DsyB0cuG7nyB0aMOgbmggbeG7mXQgdHJvbmcgMyBiaeG7g3UgdMaw4bujbmcgY+G7p2EgbmfDoG5oIHRo4buRbmcga8OqIChiw6puIGPhuqFuaCBow6xuaCBjaHXDtG5nIMO6cCB2w6AgYmnhu4N1IMSR4buTIGLDoW5oKS4gDQoNCkJp4buDdSDEkeG7kyB0aGFuaCDEkcaw4bujYyB2w60gbmjGsCBt4buZdCBjb24gZGFvIHjhur9wIFRo4buleSBT4bu5IHbDrCBi4bqhbiBjw7MgdGjhu4MgZMO5bmcgbsOzIMSR4buDIHRyw6xuaCBiw6B5IGLhuqV0IGPhu6kgxJFp4buBdSBnw6wsIHRyb25nIG3hu41pIGhvw6BuIGPhuqNuaCB2w6AgY8OzIGto4bqjIG7Eg25nIHRoYXkgdGjhur8gY2hvIG3hu41pIGxv4bqhaSBiaeG7g3UgxJHhu5Mga2jDoWMuIFPhu7EgcuG7nWkgcuG6oWMgY+G7p2EgYmnhu4N1IMSR4buTIHRoYW5oIGtoaeG6v24gbsOzIHBow7kgaOG7o3AgxJHhu4MgdHLDrG5oIGLDoHkgdOG6p24gc3XhuqV0IGhv4bq3YyB04buJIGzhu4cgY+G7p2EgYmnhur9uIMSR4buLbmggdMOtbmgvcuG7nWkgcuG6oWMuDQoNCk3hu5l0IGJp4bq/biB0aOG7gyBj4bunYSBiaeG7g3UgxJHhu5MgdGhhbmggxJHGsOG7o2MgdOG6oW8gcmEgYuG6sW5nIHPhu7EgcGjDom4ga2jDumMuIEJp4buDdSDEkeG7kyB0aGFuaCBwaMOibiBraMO6YyBjaG8gcGjDqXAgdHLDrG5oIGLDoHkgMSBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyBZICh0aMOtIGThu6UgdOG6p24gc3XhuqV0KSB0csOqbiB0cuG7pWMgdHVuZyBwaOG7pSB0aHXhu5ljIHbDoG8gMSBiaeG6v24gcuG7nWkgcuG6oWMgWDEgdHLDqm4gdHLhu6VjIGhvw6BuaCB2w6AgxJHGsOG7o2MgxJFp4buBdSBraeG7h24gaMOzYSB0aGVvIDEgeeG6v3UgdOG7kSBwaMOibiBuaMOzbSBYMiBraMOhYy4gTmjGsCB24bqteSBiaeG7g3UgxJHhu5MgbsOgeSBjaG8gcGjDqXAgYmnhu4N1IGRp4buFbiDEkeG7k25nIHRo4budaSAzIGNoaeG7gXUgdGjDtG5nIHRpbi4NCg0KS2hpIHRy4bulYyB0dW5nIGJp4buDdSBkaeG7hW4gdOG7iSBs4buHICUsIGvDrWNoIHRoxrDhu5tjIGPhu6dhIGPDoWMgdGhhbmggdHLDqm4gYmnhu4F1IMSR4buTIMSRxrDhu6NjIGNodeG6qW4gaMOzYSAoYuG6sW5nIG5oYXUpLCB0YSBz4bq9IGPDsyBt4buZdCBk4bqhbmcgYmnhu4N1IMSR4buTIMSR4bq3YyBiaeG7h3QgLSAgY8OzIGPDtG5nIGThu6VuZyB0csOsbmggYsOgeSB04buJIGzhu4cgcGjDom4gYuG7kSAoJSkgY8OhYyBuaMOjbiBnacOhIHRy4buLIGPhu6dhIG3hu5l0IGJp4bq/biBy4budaSBy4bqhYy4gROG7ryBsaeG7h3UgxJHGsOG7o2MgbeG7mXQgaMOgbSB0aOG7kW5nIGvDqiBob8OhbiBjaHV54buDbiB0aMOgbmggdOG7iSBs4buHICUgKHThuqduIHN14bqldCBoaeG7h24gZGnhu4duL3Thu5VuZyBz4buRIHRyxrDhu51uZyBo4bujcCksIHNhdSDEkcOzIHThu4kgbOG7hyBt4buXaSBuaMOjbiBnacOhIHRy4buLIChwaMOibiBuaMOzbSkgxJHGsOG7o2MgYmnhu4N1IHRo4buLIGLhu59pIGvDrWNoIHRoxrDhu5tjIDEgY+G6oW5oIGjDrG5oIGjhu5lwIGNo4buvIG5o4bqtdC4gQ8OhYyBo4buZcCBjaOG7ryBuaOG6rXQgbsOgeSDEkcaw4bujYyB44bq/cCBu4buRaSB0aeG6v3AgbmhhdSB2w6AgxJHGsOG7o2MgcGjDom4gYmnhu4d0IGLhurFuZyBtw6B1IG7hu4FuICht4buZdCBwaOG7lSBtw6B1IHLhu51pIHLhuqFjKS4gRG8gY+G6oW5oIGPDsm4gbOG6oWkgY+G7p2Egbmjhu69uZyBo4buZcCBjaOG7ryBuaOG6rXQgbmjhu48gbsOgeSBsw6AgxJHhu5NuZyBrw61jaCB0aMaw4bubYywgbsOqbiBoaeG7h3Ug4bupbmcgdMawxqFuZyBwaOG6o24gaG/DoG4gdG/DoG4gZG8gZGnhu4duIHTDrWNoIGPhu6dhIG3hu5dpIGjDrG5oIGjhu5lwIChL4bq/dCBxdeG6oyAoJSkgdOG7iSBs4buHIHRodeG6rW4gduG7m2kgZGnhu4duIHTDrWNoIGPhu6dhIG3hu5dpIGjDrG5oIGjhu5lwKS4NCg0KVOG7qyBiaeG7g3UgxJHhu5MgdGhhbmggcGjDom4ga2jDumMsIHRhIGPDsyB0aOG7gyB04bqhbyByYSBiaeG7g3UgxJHhu5MgYsOhbmggaG/hurdjIENveC1Db21iIChob8OhbiBjaHV54buDbiBo4buHIHRy4bulYyB04buNYSDEkeG7mSBEZXNjYXJ0ZXMgdGjDoG5oIFBvbGFyKS4NCg0KTuG6v3UgZMO5bmcgYmnhu4N1IMSR4buTIHRoYW5oIHRhIHPhur0gY8OzIGjDrG5oIOG6o25oIG5oxrAgc2F1Og0KDQpgYGB7cn0NCmRhdCU+JWdhdGhlcihCZW5oQTpCZW5oRCx2YWx1ZT0iQ2hhbmRvYW4iLGtleT0iQmVuaCIpJT4lDQogIGdncGxvdChhZXMoeD1CZW5oLGZpbGw9Q2hhbmRvYW4pKSsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIscG9zaXRpb249ImRvZGdlIixzaG93LmxlZ2VuZCA9IFQsYWxwaGE9MC43LGNvbD0iYmxhY2siKSsNCiAgdGhlbWVfYncoKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImJsdWUiLCJyZWQiKSkNCmBgYA0KDQpIb+G6t2MgbmjGsCB0aOG6vyBuw6B5Og0KDQpgYGB7cn0NCmRhdCU+JWdhdGhlcihCZW5oQTpCZW5oRCx2YWx1ZT0iQ2hhbmRvYW4iLGtleT0iQmVuaCIpJT4lDQogIGdncGxvdChhZXMoeD1CZW5oLGZpbGw9Q2hhbmRvYW4pKSsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIscG9zaXRpb249ImZpbGwiLHNob3cubGVnZW5kID0gVCxhbHBoYT0wLjcsY29sPSJibGFjayIpKw0KICB0aGVtZV9idygpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiYmx1ZSIsInJlZCIpKQ0KYGBgDQoNCkPDsm4gxJHDonkgbMOgIGvhur90IHF14bqjIGPhu6dhIDQgYmnhu4N1IMSR4buTIGLDoW5oOg0KDQpgYGB7cn0NCmJsYW5rX3RoZW1lIDwtIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUoDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZD1lbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE0LCBmYWNlPSJib2xkIikNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0JT4lZ2F0aGVyKEJlbmhBOkJlbmhELHZhbHVlPSJDaGFuZG9hbiIsa2V5PSJCZW5oIiklPiUNCiAgZ2dwbG90KGFlcyh4PSIiLGZpbGw9Q2hhbmRvYW4pKSsNCiAgZ2VvbV9iYXIod2lkdGggPSAxLHN0YXQgPSAiY291bnQiLHNob3cubGVnZW5kID0gVCxhbHBoYT0wLjcsY29sPSJibGFjayIpKw0KICBjb29yZF9wb2xhcih0aGV0YT0ieSIpKw0KICBzY2FsZV95X2Rpc2NyZXRlKGJyZWFrcz1OVUxMLGxhYmVscyA9IE5VTEwpKw0KICBibGFua190aGVtZSsNCiAgICBmYWNldF93cmFwKH5CZW5oLG5jb2w9NCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibHVlIiwicmVkIikpDQoNCmBgYA0KDQpUdXkgbmhpw6puLCBuaOG7r25nIGJp4buDdSDEkeG7kyBr4buDIHRyw6puIGNo4buJIG3hu5tpIGNobyBwaMOpcCBjaMO6bmcgdGEgbcO0IHThuqMgdsOgIHNvIHPDoW5oLCBob+G6t2MgdOG7kWkgxJFhIGzDoCBi4bqjbmcgY2jDqW8gMiBjaGnhu4F1LCBuaMawbmcgY2jGsGEgdGjhu7FjIHPhu7EgdGhheSB0aOG6vyDEkcaw4bujYyBjaG8gYuG6o25nIGNow6lvIDMtNCBjaGnhu4F1LCB2w6wgY2jDum5nIGtow7RuZyB0csOsbmggYsOgeSDEkcaw4bujYyBt4buRaSBsacOqbiBo4buHIGdp4buvYSA0IGLhu4duaCBsw70uIE3hu6VjIHRpw6p1IHhhIGjGoW4gY+G7p2EgY2jDum5nIHRhIGzDoCBjaHV54buDbiBi4bqjbmcgY2jDqW8gMngyeDJ4MiB0aMOgbmggYmnhu4N1IMSR4buTLiBDw7MgdGjhu4MgbMOgbSDEkWnhu4F1IG7DoHkgbmjhu50gdsOgbyA2IG5ow7NtIGPDtG5nIGPhu6UgbmjGsCBzYXU6DQoNCiMgSMOgbSBmYWNldCBncmlkOg0KDQpLaGkga+G6v3QgaOG7o3AgZ2nhu69hIGJp4buDdSDEkeG7kyB0aGFuaCB2w6AgMSBow6BtIGZhY2V0X2dyaWQoKSDigJMgxJFp4buBdSBraeG7h24gaMOzYSB0aGVvIDIgYmnhur9uIOKAkyB0YSBjw7MgdGjhu4MgdHLDrG5oIGLDoHkgxJHGsOG7o2MgbeG7mXQgYuG6o25nIGNow6lvIHThu5FpIMSRYSBsw6AgNCBjaGnhu4F1LCB0aMOtIGThu6UgMngyeDJ4MiBuaMawIHRyb25nIHRyxrDhu51uZyBo4bujcCBuw6B5LiANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRhdCU+JWdncGxvdChhZXMoZmlsbD1CZW5oQiwNCiAgICAgICAgICAgICAgICB4PUJlbmhBKSkrDQogIGdlb21fYmFyKHN0YXQ9ImNvdW50IiwNCiAgICAgICAgICAgcG9zaXRpb249ImZpbGwiLA0KICAgICAgICAgICBjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICBhbHBoYT0wLjcsDQogICAgICAgICAgIHNob3cubGVnZW5kID0gVCkrDQogIHRoZW1lX2J3KCkrDQogIGZhY2V0X2dyaWQoQmVuaEN+QmVuaEQpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwicmVkIikpDQpgYGANCg0KYGBge3J9DQpkYXQlPiVnZ3Bsb3QoYWVzKGZpbGw9QmVuaEIsDQogICAgICAgICAgICAgICAgeD1CZW5oQSkpKw0KICBnZW9tX2JhcihzdGF0PSJjb3VudCIsDQogICAgICAgICAgIHBvc2l0aW9uPSJkb2RnZSIsDQogICAgICAgICAgIGNvbG9yPSJibGFjayIsDQogICAgICAgICAgIGFscGhhPTAuNywNCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBUKSsNCiAgdGhlbWVfYncoKSsNCiAgZmFjZXRfZ3JpZChCZW5oQ35CZW5oRCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCJyZWQiKSkNCmBgYA0KDQojIEJp4buDdSDEkeG7kyBraOG6o20gKE1vc2FpYyBwbG90KQ0KDQpLaOG6o20gKG1vc2FpYykgbMOgIG3hu5l0IHBoxrDGoW5nIHBow6FwIHRyYW5nIHRyw60gdHJvbmcgbeG7uSB0aHXhuq10IHbDoCBraeG6v24gdHLDumMsIHbhu5tpIGPGoSBjaOG6vyBz4bqvcCB44bq/cCBuaOG7r25nIG3huqNuaCBnaMOpcCBi4bqxbmcgduG6rXQgbGnhu4d1IMSRw6EsIGfhu5FtIHPhu6kgaG/hurdjIHRo4buneSB0aW5oIGPDsyBow6xuaCB0aMO5IHbDoCBtw6B1IHPhuq9jIGtow6FjIG5oYXUgxJHhu4MgbOG6pXAgxJHhuqd5IG3hu5l0IG3hurd0IHBo4bqzbmcgKGPhu61hIHPhu5UsIG7hu4FuIG5ow6AsIHTGsOG7nW5nLCB0cmFuaC4uLikuIEJp4buDdSDEkeG7kyBtb3NhaWMgw6FwIGThu6VuZyBjxqEgY2jhur8gdMawxqFuZyB04buxIMSR4buDIGjDrG5oIOG6o25oIGjDs2EgbeG7mXQgYuG6o25nIHTGsMahbmcgcXVhbiB04bqnbiBz4buRIGdp4buvYSBuaGnhu4F1IGJp4bq/biDEkeG7i25oIHTDrW5oLg0KDQpCaeG7g3UgxJHhu5MgTW9zYWljIMSRxrDhu6NjIHRoaeG6v3QgbOG6rXAgbeG7mXQgY8OhY2ggY2jDrW5oIHRo4bupYyBi4bufaSAyIHTDoWMgZ2nhuqMgSGFydGlnYW4gdsOgIEtsZWluZXIgdsOgbyBuxINtIDE5ODEsIHR1eSBuaGnDqm4gdGhlbyB0w6FjIGdp4bqjIE1pY2hlYWwgRnJpZW5kbHkgKDIwMTEpLCBiaeG7g3UgxJHhu5MgbsOgeSBjw7Mgbmd14buTbiBn4buRYyBj4buVIHjGsGEgaMahbiBy4bqldCBuaGnhu4F1LiBD4bulIHRo4buDIGzDoCB04burIG7Eg20gMTY5MywgRWRtdW5kIEhhbGxleSDEkcOjIGPDsyDDvSB0xrDhu59uZyB0csOsbmggYsOgeSBk4buvIGxp4buHdSBkxrDhu5tpIGjDrG5oIHRo4bupYyBjaGnhu4F1IGNhbyB4IGNoaeG7gXUgcuG7mW5nIGPhu6dhIGjDrG5oIGNo4buvIG5o4bqtdCAoZ2nhuqNuIMSR4buTIEhhbGxleSkuIFNhdSDEkcOzLCDDvSB0xrDhu59uZyBkw7luZyBkaeG7h24gdMOtY2ggaMOsbmggcGjhurNuZyDEkeG7gyB0csOsbmggYsOgeSBk4buvIGxp4buHdSBjxaluZyDEkcaw4bujYyBuaGnhu4F1IHTDoWMgZ2nhuqMga2jDoWMgc+G7rSBk4bulbmcgbmjGsCBDaGFybGVzIGRlIEZvdXJjcm95IChQaMOhcCwgMTc4MCksIEF1Z3VzdCBGcmllZHJpY2ggV2lsaGVtIENyb21lICjEkOG7qWMsIDE3ODUpLiBW4bubaSBz4buxIHh14bqldCBoaeG7h24gY+G7p2Egbmhp4buBdSBiaeG7g3UgxJHhu5MgdGjhu5FuZyBrw6ogY8ahIGLhuqNuLCB0cm9uZyDEkcOzIGPDsyBiaeG7g3UgxJHhu5MgdGhhbmggdsOgbyDEkeG6p3UgdGjhur8ga+G7iSAxOSwgY8OzIHRo4buDIHhlbSBiaeG7g3UgxJHhu5MgTW9zYWljIGzDoCBz4buxIGJp4bq/biB04bqldSB04burIGJp4buDdSDEkeG7kyB0aGFuaCB2w6AgYmnhu4N1IMSR4buTIHRoYW5oIHBow6JuIGtow7pjIGLhurFuZyBjw6FjaCB04bqtbiBk4bulbmcgY+G6oyAyIGNoaeG7gXUgaMOsbmggaOG7mXAgdsOgIGRp4buHbiB0w61jaCDEkeG7gyBiaeG7g3UgZGnhu4VuIHRow7RuZyB0aW4uIFRow60gZOG7pSBKYWNxdWVzIGJlcnRpbGxvbiAoUGjDoXAsIDE4OTYpIMSRw6MgZMO5bmcgaMOsbmggaOG7mXAgY2jhu5NuZyBsw6puIGLhuqNuIMSR4buTLCBDaGFybGVzIEpvc2VwaCBNaW5hcmQgKDE4NDQpIHjhur9wIGPDoWMgYmnhu4N1IMSR4buTIHRoYW5oIHBow6JuIGtow7pjIHPDoXQgduG7m2kgbmhhdSB0aMOgbmggbeG7mXQgYuG6o25nIGNow6lvLiBCaeG7g3UgxJHhu5MgTW9zYWljIHPGoSBraGFpIG5o4bqldCBjw7MgbOG6vSBsw6Agc+G6o24gcGjhuqltIGPhu6dhIEdlb3JnIHZvbiBNYXlyICgxODc3KSwgbsOzIGPDsyBu4buZaSBkdW5nIGzDoCBt4buZdCBi4bqjbmcgY2jDqW8gM3gzIGNobyAyIGJp4bq/biwgduG7m2kgY8O5bmcgbmd1ecOqbiB04bqvYyBuaMawIG1vc2FpYyBwbG90IG3DoCB0YSBiaeG6v3QgbmfDoHkgbmF5Lg0KDQpCaeG7g3UgxJHhu5MgbW9zYWljIHBsb3QgYmFvIGfhu5NtIG3hu5l0IGRp4buHbiB0w61jaCBow6xuaCB2dcO0bmcgaG/hurdjIGNo4buvIG5o4bqtdCAoa2jDtG5nIGdpYW4gMiBjaGnhu4F1KSDEkcaw4bujYyBjaGlhIHRow6BuaCBuaGnhu4F1IG3huqNuaCBow6xuaCBo4buZcCBjaOG7ryBuaOG6rXQgdHJvbmcgxJHDsyBrw61jaCB0aMaw4bubYyBt4buZdCBj4bqhbmggKHRow60gZOG7pSBjaGnhu4F1IHLhu5luZykgdMawxqFuZyDhu6luZyB24bubaSB04bqnbiBzdeG6pXQgaGF5IHThu4kgbOG7hyBt4buXaSBi4bqtYyBnacOhIHRy4buLIGPhu6dhIDEgYmnhur9uIFgxLiBN4buXaSBt4bqjbmggaMOsbmggaOG7mXAgbsOgeSBzYXUgxJHDsyBs4bqhaSDEkcaw4bujYyBjaGlhIHRow6BuaCBjw6FjIG3huqNuaCBuaOG7jyBoxqFuICh0aWxlKSBtw6Aga8OtY2ggdGjGsOG7m2MgKGNoaeG7gXUgY2FvKSBt4buXaSBt4bqjbmggdGjhu4MgaGnhu4duIHThuqduIHN14bqldC904buJIGzhu4cgY2hvIG3hu5l0IGJp4bq/biB0aOG7qSAyIChYMikuIFF1eSB0csOsbmggcGjDom4gY2hpYSBuw6B5IGPDsyB0aOG7gyB0aeG6v3AgZGnhu4VuIGNobyBuaGnhu4F1IGJp4bq/biBraMOhYyBu4buvYSDigKYg4bueIHThu6tuZyBj4bqlcCDEkeG7mSBwaMOibiBjaGlhLCBkaeG7h24gdMOtY2ggY+G7p2EgbeG7l2kgbeG6o25oIGdow6lwICh0aWxlKSB04buJIGzhu4cgduG7m2kgZ2nDoSB0cuG7iyBt4buXaSDDtCB0cm9uZyBt4buZdCBi4bqjbmcgY2jDqW8gKGLhuqNuZyBwaMOibiBwaOG7kWkgdOG6p24gc+G7kSkgdOG7lSBo4bujcCBnaeG7r2EgMi0zIGJp4bq/biDEkcaw4bujYyBraOG6o28gc8OhdC4gTmjGsCB24bqteSBjw7MgdGjhu4MgbsOzaSBy4bqxbmcgYmnhu4N1IMSR4buTIG1vc2FpYyBjaMOtbmggbMOgIGjDrG5oIOG6o25oIGPhu6dhIG3hu5l0IGLhuqNuZyBjaMOpbyBraOG6o28gc8OhdCBt4buRaSBsacOqbiBo4buHL3TGsMahbmcgdMOhYyBnaeG7r2EgMiBoYXkgbmhp4buBdSBiaeG6v24gxJHhu4tuaCB0w61uaC4gTuG6v3UgY8OhYyBiaeG6v24gbsOgeSDEkeG7mWMgbOG6rXAgduG7m2kgbmhhdSAoa2jDtG5nIGPDsyBt4buRaSBsacOqbiBo4buHKSwgc+G6vSBraMO0bmcgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IMSRw6FuZyBr4buDIHbhu4EgdOG6p24gc3XhuqV0IGdp4buvYSBjw6FjIMO0IHbDoCBjw6FjIG3huqNuaCBnaMOpcCBow6xuaCBjaOG7ryBuaOG6rXQgc+G6vSBzb25nIHNvbmcgdsOgIMSR4buTbmcgZOG6oW5nOyB0csOhaSBs4bqhaSBraGkgY8OzIG3hu5FpIGxpw6puIGjhu4cgZ2nhu69hIDItMyBiaeG6v24gdHJvbmcgYuG6o25nIGNow6lvLCBjw6FjIG3huqNuaCBnaMOpcCBz4bq9IGPDsyBkaeG7h24gdMOtY2ggdG8gbmjhu48ga2jDoWMgbmhhdSwgdOG6oW8gbsOqbiBow6xuaCDhuqNuaCBs4buHY2ggc28gbGXigKYNCg0KQ8OzIHRo4buDIHbhur0gbW9zYWljIHBsb3QgcuG6pXQgZOG7hSBkw6BuZyB04burIG3hu5l0IGLhuqNuZyBjaMOpbyBuaOG7nSBwYWNrYWdlIHZjZDoNCg0KYGBge3J9DQp4dGIyPC14dGFicyh+QmVuaEErQmVuaEIrQmVuaEMrQmVuaEQsZGF0YT1kYXQpDQoNCnZjZDo6YXNzb2MoeHRiMiwgc2hhZGU9VFJVRSkNCg0KdmNkOjptb3NhaWMoeHRiMixzaGFkZT1UKQ0KYGBgDQoNCk3hu5l0IGPDoWNoIHThu5VuZyBxdcOhdCwgY8ahIGNo4bq/IGPhu6dhIGJp4buDdSDEkeG7kyBNb3NhaWMgbMOgIG3hu5l0IGjDoG0gduG7m2kgbuG7mWkgZHVuZzogV2VpZ2h0IH4gdOG6rXAgYmnhur9uIFhqLCB24bubaSBXZWlnaHQgbMOgIHRy4buNbmcgc+G7kSBkaeG7h24gdMOtY2guIFRyxrDhu51uZyBo4bujcCDEkcahbiBnaeG6o24gbmjhuqV0LCB0YSBjaOG7iSBjw7MgMSBiaeG6v246IFdlaWdodCB+IFgsIGJp4buDdSDEkeG7kyBNb3NhaWMgY2jDrW5oIGzDoCBt4buZdCBiaeG7g3UgxJHhu5MgdGhhbmggcGjDom4ga2jDumMgKHN0YWNrZWQgYmFyIGdyYXBoKS4gDQoNClBhY2thZ2UgZ2dtb3NhaWMgY2hvIHBow6lwIHTDuXkgY2jhu4luaCBu4buZaSBkdW5nIGPhu6dhIGjDoG0gbsOgeSB2w6AgdMOtY2ggaOG7o3AgYmnhu4N1IMSR4buTIE1vc2FpYyB2w6BvIG5n4buvIHBow6FwIGdncGxvdDIgcXVhIGjDoG0gZ2VvbV9tb3NhaWMoICkuIA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2dtb3NhaWMpDQoNCmRhdCU+JWdncGxvdCgpK2dlb21fbW9zYWljKGFlcyh3ZWlnaHQgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwcm9kdWN0KEJlbmhCLEJlbmhDKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsPUJlbmhBKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPSJibGFjayIsbmEucm09VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgb2Zmc2V0ID0gMC4wMSkrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsInJlZCIpKSsNCiAgdGhlbWVfYncoKStmYWNldF9ncmlkKEJlbmhEfi4pK2Nvb3JkX2ZsaXAoKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdnbW9zYWljKQ0KDQpkYXQlPiVnZ3Bsb3QoKStnZW9tX21vc2FpYyhhZXMod2VpZ2h0ID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcHJvZHVjdChCZW5oQixCZW5oQyxCZW5oRCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1CZW5oQSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD0iYmxhY2siLG5hLnJtPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG9mZnNldCA9IDAuMDEpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCJyZWQiKSkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIEJp4buDdSDEkeG7kyBsxrB1IGzGsOG7o25nIChGbG93IGRpYWdyYW0pDQoNCk7hur91IHRhIGjDrG5oIGR1bmcgbeG7l2kgw7QgdHJvbmcgYuG6o25nIGNow6lvIGzDoCBt4buZdCBuaMOhbmggc8O0bmcvc3Xhu5FpIGhheSBuZ8SDbiBjaOG7qWEgdHJvbmcgbeG7mXQgY8OhaSBo4buTLCBjw7JuIHThuq1wIGjhu6NwIDUwMCBjw6EgdGjhu4MgYuG7h25oIG5ow6JuIG5oxrAgbeG7mXQga2jhu5FpIGNo4bqldCBs4buPbmcgKG7GsOG7m2MpLCB0aMOsIHPhu7EgcGjDom4gYuG7kSB04bqnbiBzdeG6pXQgdsOgbyBjw6FjIMO0IHRyb25nIGLhuqNuZyBjaMOpbyBjxaluZyBnaeG7kW5nIG5oxrAgcsOzdCBuxrDhu5tjIHbDoG8gdOG7q25nIG5nxINuIGhv4bq3YyBt4buZdCBkw7JuZyBjaOG6o3kgY+G7p2Egbsaw4bubYyBxdWEgbeG6oW5nIGzGsOG7m2kgc8O0bmcvc3Xhu5FpIG7DoHkuIEPDsyBt4buZdCBk4bqhbmcgxJHhu5MgaOG7jWEgxJHhurdjIGJp4buHdCBjaG8gcGjDqXAgbcO0IHThuqMgZMOybmcgY2jhuqN5IG7DoHksIMSRw7MgbMOgIGPDoWMgYmnhu4N1IMSR4buTIGzGsHUgbMaw4bujbmcgKGZsb3cgZGlhZ3JhbSkuIA0Kw50gdMaw4bufbmcgduG7gSBmbG93IGRpYWdyYW0gZG8gQ2hhcmxlcyBNaW5hcmQgcGjDoXQgbWluaCB2w6BvIG7Eg20gMTg2OSAoYmnhu4N1IMSR4buTIGjDoG5oIHF1w6JuIGPhu6dhIE5hcG9sZW9uIHbDoG8gbsaw4bubYyBOZ2EpLg0KDQpGbG93IGRpYWdyYW0gxJHGsOG7o2MgYmnhur90IGTGsOG7m2kgbmhp4buBdSBjw6FpIHTDqm4ga2jDoWMgbmhhdSBuaMawIGJp4buDdSDEkeG7kyBTYW5rZXkgKHThu6sgdMOqbiBj4bunYSB0w6FjIGdp4bqjIE1hdHRoZXcgSFAuIFNhbmtleSDEkcOjIGTDuW5nIGJp4buDdSDEkeG7kyBuw6B5IHbDoG8gbsSDbSAxODk4IMSR4buDIG3DtCB04bqjIGTDsm5nIG7Eg25nIGzGsOG7o25nIHF1YSBt4buZdCDEkeG7mW5nIGPGoSBoxqFpIG7GsOG7m2MpLCBob+G6t2MgQWxsdXZpYWwgZGlhZ3JhbSwgbMOgIG3hu5l0IGThuqFuZyBmbG93IGRpYWdyYW0gxJHhurdjIGJp4buHdCBtw7QgdOG6oyBz4buxIHRoYXkgxJHhu5VpIGPhuqV1IHRyw7pjIGPhu6dhIG3hu5l0IGjhu4cgdGjhu5FuZyAobmV0d29yaykgdGhlbyB0aOG7nWkgZ2lhbiAodGhheSB2w6wga2jDtG5nIGdpYW4gbmjGsCBTYW5rZXkgcGxvdCkuIA0KDQpGbG93IGRpYWdyYW0gxJHGsOG7o2Mgc+G7rSBk4bulbmcgbmhp4buBdSB0cm9uZyBuZ8OgbmggVuG6rXQgbMO9LCDEkOG7i2EgbMO9LCBUaOG7p3kgbOG7o2ksIEPDtG5nL07DtG5nIG5naGnhu4dwIHbDoCBraW5oIHThur8uIFR1eSBuaGnDqm4gw70gdMaw4bufbmcgY+G7p2EgbsOzIGPDsyB0aOG7gyDDoXAgZOG7pW5nIHThu5VuZyBxdcOhdCBjaG8gbeG7jWkgduG6pW4gxJHhu4EgbGnDqm4gcXVhbiDEkeG6v24gcGjDom4gYuG7kSB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uIMSR4buLbmggdMOtbmgvcGjDom4gbmjDs20uDQoNClRhIGPDsyB0aOG7gyB24bq9IGJp4buDdSDEkeG7kyBTYW5rZXkgaGF5IGFsbHV2aWFsIGLhurFuZyBwYWNrYWdlIGdnYWxsdXZpYWwgdHJvbmcgUjoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkoZ2dhbGx1dmlhbCkNCmxpYnJhcnkocGFscykNCg0KeHRiPWRhdCU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoQmVuaEEsQmVuaEIsQmVuaEMsQmVuaEQpJT4lDQogIHN1bW1hcmlzZShmcmVxdWVuY3kgPSBuKCkpDQoNCnh0YiU+JWdncGxvdChhZXMod2VpZ2h0ID0gZnJlcXVlbmN5LCANCiAgICAgICAgICAgICAgICAgIGF4aXMxID0gQmVuaEEsIA0KICAgICAgICAgICAgICAgICBheGlzMj0gQmVuaEIsDQogICAgICAgICAgICAgICAgIGF4aXMzPUJlbmhDLA0KICAgICAgICAgICAgICAgIGF4aXM0ID0gQmVuaEQpKSArDQogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGwgPSBmcmVxdWVuY3kpLCB3aWR0aCA9IDAuMykgKw0KICBnZW9tX3N0cmF0dW0od2lkdGggPSAxLzEwLCANCiAgICAgICAgICAgICAgIGZpbGwgPSAid2hpdGUiLCANCiAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiKSArDQogIGdlb21fbGFiZWwoc3RhdCA9ICJzdHJhdHVtIiwgDQogICAgICAgICAgICAgbGFiZWwuc3RyYXRhID0gVFJVRSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo0LCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIsIkMiLCJEIikpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gcGFsczo6cGFydWxhKG49NTAwKSkrDQogIHRoZW1lX2J3KCkrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KDQoNCg0KeHRiJT4lZ2dwbG90KGFlcyh3ZWlnaHQgPSBmcmVxdWVuY3ksIA0KICAgICAgICAgICAgICAgICBheGlzMSA9IEJlbmhBLCANCiAgICAgICAgICAgICAgICAgYXhpczI9IEJlbmhCLA0KICAgICAgICAgICAgICAgICBheGlzMz1CZW5oQywNCiAgICAgICAgICAgICAgICBheGlzNCA9IEJlbmhEKSkrDQogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGwgPUJlbmhBKSwNCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAsIA0KICAgICAgICAgICAgICAgIHJldmVyc2UgPSBGQUxTRSkgKw0KICBndWlkZXMoZmlsbCA9IEZBTFNFKSsNCiAgZ2VvbV9zdHJhdHVtKHdpZHRoID0gMS8xMCwgDQogICAgICAgICAgICAgICByZXZlcnNlID0gRkFMU0UpKw0KICBnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgDQogICAgICAgICAgICBsYWJlbC5zdHJhdGEgPSBUUlVFLCANCiAgICAgICAgICAgIHJldmVyc2UgPSBGQUxTRSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo0LCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkJlbmhBIiwiQmVuaEIiLCAiQmVuaEMiLCAiQmVuaEQiKSkrDQogIGNvb3JkX2ZsaXAoKSt0aGVtZV9idygpDQoNCmBgYA0KDQojIEJp4buDdSDEkeG7kyBkw6J5IGN1bmcNCg0KxJDDonkgbMOgIG3hu5l0IHBow6F0IGtp4bq/biBt4bubaSB0cm9uZyBuZ8OgbmggxJHhu5MgaOG7jWEgdGjhu5FuZyBrw6ogKG7DsyDEkcaw4bujYyDDoXAgZOG7pW5nIGzhuqduIMSR4bqndSB0acOqbiB2w6BvIG7Eg20gMjAwNyDEkeG7gyBtaW5oIGjhu41hIHbhu4EgbeG6oW5nIGzGsOG7m2kgcXVhbiBo4buHIHBo4bupYyB04bqhcCBnaeG7r2EgY8OhYyBnZW5lIHRyb25nIGLhu5kgZ2VuZS4NCg0Kw50gdMaw4bufbmcgY+G7p2EgY2hvcmQgZGlhZ3JhbSBwaMOhdCB4deG6pXQgdOG7qyBraMOhaSBuaeG7h20g4oCcZMOieSBjdW5n4oCdIHRyw6puIMSRxrDhu51uZyB0csOybiB0cm9uZyBtw7RuIGjDrG5oIGjhu41jIHBo4bqzbmcgKG3hu5l0IMSRb+G6oW4gdGjhurNuZyBjw7MgMiDEkeG6p3UgxJHhu4F1IG7hurFtIHRyw6puIMSRxrDhu51uZyB0csOybikuIELhuqNuIGNo4bqldCBj4bunYSBjaG9yZCBkaWFncmFtIGzDoCBt4buZdCBiaeG7g3UgxJHhu5MgbeG6oW5nIChuZXR3b3JrKSDEkcaw4bujYyBz4bqvcCB44bq/cCB0aGVvIGjDrG5oIHRyw7JuLCBkbyDEkcOzIG7DsyBjw7MgdGjhu4MgxJHGsOG7o2MgeOG6v3AgdsOgbyBo4buNIGJp4buDdSDEkeG7kyBow6xuaCB0csOybiAoY2lyY3VsYXIgbGF5b3V0IGdyYXBoKS4NCkPGoSBjaOG6vyBj4bunYSBjaG9yZCBkaWFncmFtIG5oxrAgc2F1OiBE4buvIGxp4buHdSDEkcaw4bujYyBz4bqvcCB44bq/cCB0csOqbiDEkcaw4budbmcgdHLDsm4gdGjDoG5oIHThu6tuZyBjdW5nIChwaMOibiBuaMOzbSkgaG/hurdjIMSRaeG7g20gKGPDoSB0aOG7gyksIHNhdSDEkcOzIGxpw6puIGjhu4cgZ2nhu69hIGPDoWMgcGjDom4gbmjDs20vY8OhIHRo4buDIG7DoHkgxJHGsOG7o2MgZGnhu4VuIHThuqMgYuG6sW5nIG5o4buvbmcgZMOieSBjdW5nLg0KDQpW4buBIMO9IG5naMSpYSB0cnV54buBbiB0aMO0bmcsIGNob3JkIGRpYWdyYW0gdOG7kXQgY+G6oyB24buBIGzGsOG7o25nIGzhuqtuIHbhu4EgcGjhuqltOiBuw7MgY2hvIHBow6lwIHThuqNpIHPhu5EgbMaw4bujbmcgdGjDtG5nIHRpbiBy4bqldCBs4bubbiAodsO0IHPhu5EgbmjDs20vY8OhIHRo4buDKSBjw7MgdGjhu4MgxJHGsOG7o2MgeOG6v3AgbuG7kWkgdGnhur9wIHRyw6puIGN1bmcgdHLDsm4pLiBDaG9yZCBkaWFncmFtIHRyw6xuaCBiw6B5IMSRxrDhu6NjIGPhuqMgdGjDtG5nIHRpbiB24buBIGPhuqV1IHRyw7pjIHbDoCBz4buxIHTGsMahbmcgcGjhuqNuLiBEaeG7h24gdMOtY2ggKMSR4buZIGThuqd5KSBj4bunYSBt4buXaSBkw6J5IGPDsm4gY2hvIGJp4bq/dCB04bqnbiBzdeG6pXQgY+G7p2EgdOG7lSBo4bujcCBjxaluZyBuaMawIMSR4buZIG3huqFuaCBj4bunYSBt4buRaSBsacOqbiBr4bq/dC4gTmdvw6BpIHJhLCBjaG9yZCBkaWFncmFtIGPDsm4gxJHGsOG7o2MgxrBhIGNodeG7mW5nIHbDrCB0w61uaCBt4bu5IHRodeG6rXQ6IG3DoHUgc+G6r2MgdsOgIHPhu7EgY2jhu5NuZyBs4bqvcCBj4bunYSBuaGnhu4F1IGTDonkgY3VuZyBtYW5nIGzhuqFpIG3hu5l0IGhp4buHdSDhu6luZyDEkeG6uXAgcXV54bq/biByxakuDQoNCkPDoWMgYuG6oW4gY8OzIHRo4buDIHRoYW0ga2jhuqNvIHF1eeG7g24gc8OhY2gg4oCcQ2lyY3VsYXIgVmlzdWFsaXNhdGlvbiBpbiBS4oCdIGPhu6dhIFp1Z3VhbmcgR3UgxJHhu4MgYmnhur90IHRow6ptIHbhu4EgaOG7jSBiaeG7g3UgxJHhu5MgdHLDqm4gY3VuZyB0csOybiBodHRwOi8venVndWFuZy5kZS9jaXJjbGl6ZV9ib29rL2Jvb2svDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpsaWJyYXJ5KGNpcmNsaXplKQ0KDQp4dGIzPWRhdCU+JW11dGF0ZShJZD1yb3duYW1lcyhkYXQpKSU+JWdhdGhlcihCZW5oQTpCZW5oRCxrZXk9IkJlbmgiLHZhbHVlPSJTdGF0dXMiKSU+JQ0KICBncm91cF9ieShTdGF0dXMsQmVuaCklPiUNCiAgc3VtbWFyaXNlKGZyZXF1ZW5jeSA9IG4oKSkNCg0KZ3JpZC5jb2wgPSBjKGBZYCA9InJlZCIsYE5gID0iYmx1ZSIsDQogICAgICAgICAgICAgIGBCZW5oQWAgPSJvcmFuZ2UiLGBCZW5oQmAgPSJncmVlbiIsDQogICAgICAgICAgICAgICBgQmVuaENgID0icHVycGxlIixgQmVuaERgID0icGluayIpDQoNCmNob3JkRGlhZ3JhbShhcy5kYXRhLmZyYW1lKHh0YjMpLCANCiAgICAgICAgICAgICB0cmFuc3BhcmVuY3kgPSAwLjUsZ3JpZC5jb2wgPWdyaWQuY29sKQ0KDQpgYGANCg0KIyBTxqEgxJHhu5MgVmVubg0KDQpHaeG6o24gxJHhu5MgVmVubiBoYXkgY8OybiBn4buNaSBsw6Agc8ahIMSR4buTIHThuq1wIGjhu6NwLCBsw6AgbeG7mXQgaMOsbmggdGjhu6ljIG1pbmggaOG7jWEgY2hvIGPDoWMgbGnDqm4gaOG7hyBtYW5nIHTDrW5oIGxvZ2ljIGdp4buvYSAyIGhheSBuaGnhu4F1IHThuq1wIGjhu6NwLiBOw7MgxJHGsOG7o2Mgc+G7rSBk4bulbmcgcuG6pXQgcGjhu5UgYmnhur9uIHRyb25nIG3DtG4gbG9naWMgaOG7jWMsIGzDvSB0aHV54bq/dCB4w6FjIHN14bqldCwgdGjhu5FuZyBrw6ogdsOgIGPhuqMgdHJvbmcgdHJ1eeG7gW4gdGjDtG5nIMSR4bqhaSBjaMO6bmcuIFPGoSDEkeG7kyBWZW5uIMSRxrDhu6NjIHBow6F0IG1pbmggdsOgbyBuxINtIDE4ODAgYuG7n2kgSm9obiBWZW5uLiANCg0KVHJvbmcgc8ahIMSR4buTIFZlbm4sIG3hu5dpIHThuq1wIGjhu6NwIMSRxrDhu6NjIMSR4bqhaSBkaeG7h24gYuG6sW5nIG3hu5l0IGjDrG5oIHRyw7JuIGhheSBFbGlwIGPDsyBtw6B1IHPhuq9jIHJpw6puZyBiaeG7h3QgduG7m2kgZGnhu4duIHTDrWNoIHThu4kgbOG7hyB24bubaSBz4buRIHBo4bqnbiB04butIG3DoCBuw7MgY2jhu6lhLiBQaOG6p24gY2h1bmcgZ2nhu69hIDIgaGF5IG5oaeG7gXUgdOG6rXAgaOG7o3AgY2jDrW5oIGzDoCBr4bq/dCBxdeG6oyBj4bunYSB2aeG7h2MgY2jhu5NuZyBs4bqvcCBt4buZdCBwaOG6p24gbmjhu69uZyBow6xuaCB0csOybi9lbGlwIG7DoHkgbMOqbiBuaGF1LiANCg0KVHJvbmcgUiBjw7MgaGFpIGPDoWNoIMSR4buDIHbhur0gc8ahIMSR4buTIFZlbm4sIGPDoWNoIGzDoG0gY8WpIGThu7FhIHbDoG8gcGFja2FnZSBsaW1tYSB04burIGJpb2NvbmR1Y3RvcjoNCg0KYGBge3J9DQoNCiMgc291cmNlKCJodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikNCg0KIyBiaW9jTGl0ZSgibGltbWEiKQ0KDQojIGxpYnJhcnkobGltbWEpDQoNCiMgZGF0MlssYygyOjUpXSU+JXZlbm5Db3VudHMoKSU+JXZlbm5EaWFncmFtKG5hbWVzID0gYygiQSIsIkIiLCJDIiwiRCIpLGNpcmNsZS5jb2wgPSBjKCIjZmMxMTNjIiwiIzExYTZmYyIsIiMxMWZjNmIiLCIjZmNiMjExIikpDQpgYGANCg0KIVtdKFZlbm4ucG5nKQ0KDQpN4buZdCBwaMawxqFuZyBwaMOhcCBraMOhYyBt4bubaSDEkcaw4bujYyB04bqhbyByYSBn4bqnbiDEkcOieSAodGjDoW5nIDUvMjAxOCksIMSRw7MgbMOgIHBhY2thZ2UgblZlbm5SLg0KDQpgYGB7cn0NCmxpYnJhcnkoblZlbm5SKQ0KDQp2cGxvdDwtbGlzdChBPWZpbHRlcihkYXQsQmVuaEE9PSJZIiklPiUuJEJlbmhuaGFuLA0KICAgICAgICAgICBCPWZpbHRlcihkYXQsQmVuaEI9PSJZIiklPiUuJEJlbmhuaGFuLA0KICAgICAgICAgICBDPWZpbHRlcihkYXQsQmVuaEM9PSJZIiklPiUuJEJlbmhuaGFuLA0KICAgICAgICAgICBEPWZpbHRlcihkYXQsQmVuaEQ9PSJZIiklPiUuJEJlbmhuaGFuDQopJT4lcGxvdFZlbm4oKQ0KDQpzaG93U1ZHKG5WZW5uT2JqID0gdnBsb3QsIA0KICAgICAgICBzZXRDb2xvcnMgPSBjKCIjMmNhYmY5IiwiIzJjZjliYyIsIiM5M2Y5MmMiLCIjZjljYTJjIiksDQogICAgICAgIGJvcmRlcldpZHRoID0gMyxvcGFjaXR5ID0gMC4yLA0KICAgICAgICBsYWJlbFJlZ2lvbnMgPSBGLCBmb250U2NhbGUgPSAzKQ0KYGBgDQoNCiMgTHXhuq10IGvhur90IGjhu6NwIC0gYmnhu4N1IMSR4buTIG3huqFuZw0KDQpOZ2/DoGkgbmjhu69uZyBiaeG7g3UgxJHhu5MgdGh14buZYyBs4bubcCAiZnVuY3Rpb24iIGvhu4MgdHLDqm4sIHRhIGPDsm4gY8OzIHRo4buDIHPhu60gZOG7pW5nIHBoxrDGoW5nIHBow6FwIGx14bqtdCBr4bq/dCBo4bujcCAoYXNzb2NpYXRpb24gcnVsZSkgdsOgIHThuqFvIHJhIDEgYmnhu4N1IMSR4buTIG3huqFuZyAobmV0d29yaykgbmjGsCBzYXU6DQoNCmBgYHtyfQ0KbGlicmFyeShhcnVsZXMpDQpsaWJyYXJ5KGFydWxlc1ZpeikNCg0KcnVsZXM9YXByaW9yaShkYXRbLC0xXSwgY29udHJvbCA9IGxpc3QodmVyYm9zZT1GKSwNCiAgICAgICAgICAgICAgcGFyYW1ldGVyID0gbGlzdChtaW5sZW49MiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VwcD0xMC81MDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmY9MC44KSkNCg0KcnVsZXMgPC0gc29ydChydWxlcywgYnk9ImxpZnQiKQ0KDQpyZWR1bmRhbnQ9aXMucmVkdW5kYW50KHJ1bGVzLG1lYXN1cmU9ImNvbmZpZGVuY2UiKQ0KDQpydWxlMj1ydWxlc1shcmVkdW5kYW50XQ0KDQpwbG90KHJ1bGUyLCBtZXRob2Q9ImdyYXBoIixlbmdpbmU9Imh0bWx3aWRnZXQiKQ0KYGBgDQoNCiMgS+G6v3QgbHXhuq1uDQoNCkLDoGkgdGjhu7FjIGjDoG5oIMSR4bq/biDEkcOieSBsw6AgaOG6v3QsIGPDoWMgYuG6oW4gxJHDoyBiaeG6v3QgY8OhY2ggc+G7rSBk4bulbmcgNiBjw7RuZyBj4bulIGtow6FjIG5oYXUgxJHhu4MgY2h1eeG7g24gYuG6o25nIGNow6lvIMSRYSBjaGnhu4F1IHRow6BuaCBiaeG7g3UgxJHhu5MuIFTDuXkgYuG6o24gY2jhuqV0IHbhuqVuIMSR4buBIHbDoCBwaG9uZyBjw6FjaCBjw6EgbmjDom4sIHnDqnUgY+G6p3UgbeG7uSB0aHXhuq10LCBi4bqhbiBjw7MgdGjhu4MgZMO5bmcgbeG7mXQgdHJvbmcgbmjhu69uZyBjw7RuZyBj4bulIG7DoHkgY2hvIGx14bqtbiB2xINuL2LDoGkgdGh1eeG6v3QgdHLDrG5oL3bEg24gYuG6o24ga2hvYSBo4buNYyBj4bunYSBtw6xuaCDEkeG7gyB0aGF5IHRo4bq/IGNobyBuaOG7r25nIGLhuqNuZyBjaMOpbyBy4buRaSBy4bqvbS4NCg0KQ2jDumMgY8OhYyBi4bqhbiB0aMOgbmggY8O0bmcuDQo=