Giới thiệu
Trong một cuộc thăm dò thường niên và kéo dài từ 1999-2009 của tổ chức Swiss Household Panel, 2612 người dân Thụy Sỹ đã tự đánh giá sức khỏe của bản thân theo thang điểm gồm 5 mức độ: G1=Rất khỏe mạnh (Very well), G2=Khỏe (Well), M=Trung bình (Average), B1=Không tốt và B2=Xấu (Bad).
Nội dung của dữ liệu như sau:
library(tidyverse)
library(PST)
data(SRH)
SRH%>%head()%>%knitr::kable()
4101 |
man |
1965 |
0.6870344 |
very well |
very well |
very well |
very well |
very well |
very well |
very well |
well |
very well |
very well |
well |
4102 |
woman |
1968 |
0.9610977 |
not very well |
well |
so, so (average) |
very well |
very well |
very well |
so, so (average) |
so, so (average) |
not very well |
not very well |
well |
5101 |
man |
1961 |
0.8828794 |
well |
well |
well |
well |
well |
NA |
well |
well |
very well |
well |
well |
27101 |
woman |
1968 |
0.9888913 |
so, so (average) |
so, so (average) |
well |
well |
so, so (average) |
well |
well |
well |
well |
so, so (average) |
so, so (average) |
27102 |
man |
1968 |
0.8139466 |
NA |
very well |
well |
very well |
very well |
well |
very well |
well |
so, so (average) |
well |
very well |
34101 |
woman |
1965 |
0.4232759 |
very well |
very well |
very well |
well |
very well |
NA |
well |
well |
well |
well |
well |
Thiết kế nghiên cứu này hết sức thú vị : Ở thời điểm khởi đầu, chưa có một giả thuyết xác định nào: Mục tiêu có thể là khai phá dữ liệu: tìm ra một quy luật diễn tiến (pattern) của sức khỏe của quần thể theo thời gian. Với quy luật này, ta hy vọng có thể tiên lượng được sức khỏe của một cá thể tại một thời điểm bất kì nếu biết thông tin về người này trong quá khứ.
Ý tưởng khảo sát chuỗi trạng thái/sự kiện có thể áp dụng trong nhiều lĩnh vực, thí dụ:
Thương mại: Một cửa hàng điện thoại muốn khảo sát khuynh hướng lựa chọn thay đổi smartphone của khách hàng theo thời gian, trong số các thương hiệu, từ đó có thể tiên lượng xác suất một người đang sử dụng máy Samsung sẽ chuyển sang dùng IPhone hoặc tiếp tục trung thành với thương hiệu cũ.
Y học lâm sàng: Theo dõi diễn tiến của một bệnh lý theo thời gian, thí dụ lộ trình diễn tiến của bệnh suy tim (theo NYHA), bệnh phổi tắc nghẽn mạn tính, hoặc trạng thái dinh dưỡng (bình thường/thừa cân/béo phì…) ; hoặc sự thay đổi phương pháp điều trị (chuyển từ thuốc dãn phế quản sang corticoid, từ kháng sinh thường sang kháng sinh tân tiến hơn, giữa các loại thuốc giảm đau…)
Xã hội học: Theo dõi diễn tiến của tình trạng Gia đình (Độc thân, kết hôn, có con…), học vấn/nghề nghiệp (xác suất vào đại học, và học tiếp lên cao học hay đi làm, nguy cơ thất nghiệp, sự chuyển đổi nghề, công việc), tôn giáo, …
Vấn đề này hoàn toàn mới lạ đối với nhiều bạn, và yêu cầu một phương pháp thống kê đặc biệt có tên là “Phân tích chuỗi trạng thái”, mà mục tiêu là khai thác thông tin từ dữ liệu trường diễn (longitudinal) với outcome là một biến rời rạc nhiều bậc giá trị (trạng thái hay sự kiện).
Bài thực hành hôm nay, Nhi sẽ giới thiệu với các bạn công cụ chuyên dụng cho loại nghiên cứu này, đó là package TRaMineR.
Cấu trúc dữ liệu chuỗi
Đầu tiên, chúng ta cần bàn về cấu trúc của dữ liệu cho thiết kế nghiên cứu theo dõi chuỗi trạng thái. Vấn đề là dữ liệu loại này có thể được tạo ra dưới nhiều dạng và chúng khác nhau về cấu trúc. Ngoài các thuộc tính như Giới tính, Năm sinh, … , dữ liệu chuỗi có 3 thành phần chính: Biến định danh cá thể , Thời gian/Thời điểm và trạng thái.
- STS: Dạng thứ nhất tên là State-sequence, có cấu trúc Wide, mỗi hàng là một cá thể i, cột đầu tiên để định danh, các thời điểm và trạng thái được trình bày bằng nhiều cột nối tiếp nhau, mỗi cột là một thời điểm t và mỗi ô nhận 1 giá trị trạng thái (ij)
data_frame(Id=c(1:5),T1=c("A","A","B","C","D"),
T2=c("A","B","C","D","D"),
T3=c("A","B","B","A","C")
)%>%knitr::kable()
1 |
A |
A |
A |
2 |
A |
B |
B |
3 |
B |
C |
B |
4 |
C |
D |
A |
5 |
D |
D |
C |
- SPS: State-permanence. Dạng dữ liệu này cũng có cấu trúc wide, mỗi hàng cũng trình bày 1 cá thể, nhưng mỗi cột biểu thị cho một bậc trạng thái và nhận giá trị gồm 2 thông tin (Tên trạng thái/thời gian kéo dài cho trạng thái đó).
Ưu điểm của định dạng này là cho phép khảo sát thêm thời gian như một biến liên tục, nhưng khó thao tác và quản lý.
data_frame(Id=c(1:5),State1=c("A/3",NA,"B/4","C/2","D/1"),
State2=c("C/2","B/3","A/4","D/1",NA),
State3=c("D/3","C/1",NA,"D/3","A/4")
)%>%knitr::kable()
1 |
A/3 |
C/2 |
D/3 |
2 |
NA |
B/3 |
C/1 |
3 |
B/4 |
A/4 |
NA |
4 |
C/2 |
D/1 |
D/3 |
5 |
D/1 |
NA |
A/4 |
- TSE: Time-stamped event. Trái với 2 dạng trên, TSE có cấu trúc Long, gồm 3 cột: 1 cột định danh, 1 cột để đo thời gian như con số thực sự, 1 cột chỉ sự kiện. Cấu trúc dữ liệu này rất dễ thao tác, nhập liệu và cho phép khảo sát thời gian như một biến định lượng (thí dụ tuổi, ngày/ tháng), cũng như theo dõi chuỗi sự kiện.
data_frame(Id=c(1,1,1,2,2,2,3,3,3),
Age=c(24,25,26,22,23,24,31,32,33),
Event=c("A","B","C","A","C","D","B","C","D")
)%>%knitr::kable()
1 |
24 |
A |
1 |
25 |
B |
1 |
26 |
C |
2 |
22 |
A |
2 |
23 |
C |
2 |
24 |
D |
3 |
31 |
B |
3 |
32 |
C |
3 |
33 |
D |
- SPELL: Đây là một dạng mở rộng từ TSE, nó cũng có cấu trúc Long, gồm 4 cột, một cột định danh, một cột chỉ sự kiện, 1 cột chỉ thời điểm phát sinh sự kiện và 1 cột chỉ thời gian chấm dứt sự kiện. Đây là định dạng dữ liệu linh hoạt nhất, nó cho phép khảo sát cả những sự kiện trùng hợp, xảy ra đồng thời, cũng như khảo sát được cả khoảng thời gian và thời điểm.Một biến phụ Duration có thể được tạo ra để đo khoảng thời gian sự kiện diễn ra. Cấu trúc dữ liệu này có thể được tạo ra tự động từ dữ liệu chuỗi thời gian bằng một hàm; khi biết giá trị start point và end point.
data_frame(Id=c(1,1,1,2,2,2,3,3,3),
From=c(18,23,28,16,26,33,21,35,48),
To=c(22,27,40,25,32,48,34,47,52),
Event=c("A","B","C","A","C","D","B","C","D"),
Duration=c(To-From)
)%>%knitr::kable()
1 |
18 |
22 |
A |
4 |
1 |
23 |
27 |
B |
4 |
1 |
28 |
40 |
C |
12 |
2 |
16 |
25 |
A |
9 |
2 |
26 |
32 |
C |
6 |
2 |
33 |
48 |
D |
15 |
3 |
21 |
34 |
B |
13 |
3 |
35 |
47 |
C |
12 |
3 |
48 |
52 |
D |
4 |
Trở lại với thí dụ trong bài, ta có thể thấy nó có dạng STS.
Package TraMineR cho phép chuyển đổi thoải mái giữa các định dạng dữ liệu, thí dụ ta có thể chuyển từ STS sang SPELL:
library(TraMineR)
srh_seq=seqdef(SRH[,5:15])
seqformat(srh_seq,
from = "STS", to = "SPELL",
process=F,
with.missing=TRUE)%>%head()%>%knitr::kable()
1 |
1 |
7 |
very well |
1 |
8 |
8 |
well |
1 |
9 |
10 |
very well |
1 |
11 |
11 |
well |
2 |
1 |
1 |
not very well |
2 |
2 |
2 |
well |
Hay chuyển từ STS sang SPS:
seqformat(srh_seq,
from = "STS", to = "SPS",
process=F,
with.missing=TRUE)%>%head()%>%knitr::kable()
[1] |
(very well,7) |
(well,1) |
(very well,2) |
(well,1) |
NA |
NA |
NA |
NA |
NA |
NA |
NA |
[2] |
(not very well,1) |
(well,1) |
(so, so (average),1) |
(very well,3) |
(so, so (average),2) |
(not very well,2) |
(well,1) |
NA |
NA |
NA |
NA |
[3] |
(well,5) |
(*,1) |
(well,2) |
(very well,1) |
(well,2) |
NA |
NA |
NA |
NA |
NA |
NA |
[4] |
(so, so (average),2) |
(well,2) |
(so, so (average),1) |
(well,4) |
(so, so (average),2) |
NA |
NA |
NA |
NA |
NA |
NA |
[5] |
(*,1) |
(very well,1) |
(well,1) |
(very well,2) |
(well,1) |
(very well,1) |
(well,1) |
(so, so (average),1) |
(well,1) |
(very well,1) |
NA |
[6] |
(very well,3) |
(well,1) |
(very well,1) |
(*,1) |
(well,5) |
NA |
NA |
NA |
NA |
NA |
NA |
Cũng như từ STS sang TSE
ttrans <- seqetm(srh_seq, method='transition')
seqformat(srh_seq,
from = "STS", to = "TSE",tevent=ttrans,
process=F,
with.missing=TRUE)%>%head()%>%knitr::kable()
1 |
0 |
very well |
1 |
7 |
very well>well |
1 |
8 |
well>very well |
1 |
10 |
very well>well |
2 |
0 |
not very well |
2 |
1 |
not very well>well |
Trên thực tế, do 2 định dạng SPELL và TSE là dễ quản lý nhất, nên chúng ta thường nhập liệu dưới dạng này, nhưng định dạng STS lại là mặc định cho nhiều hàm trong package, nên ta phải chuyển dạng ngược từ SPELL sang STS hay từ TSE sang STS. Để làm việc này, ta chỉ cần khai báo 3 biến: id,begin/end(cho SPELL) hay time (cho TSE) và event/status.
Thống kê mô tả
Đầu tiên, từ dữ liệu SRH, ta phải tạo ra chuỗi trạng thái/sự kiện, đây sẽ là nguyên liệu cho tất cả mọi quá trình phân tích tiếp theo nên bước này rất quan trọng:
Bên trong object chuỗi này, ta có thể xác định thêm: Nhãn tên gọi cho mỗi sự kiện/trạng thái, thí dụ B1,B2,M,G1,G2 (theo thứ tự ABC), và phổ màu dùng để vẽ biểu đồ sau này:
srh_seq<-seqdef(SRH,5:15,
states=c("B1","B2","M","G1","G2"),
labels=c("NotW","Bad","Average","VeryW","Well"),
cpal=pals::brewer.rdylgn(5))
summary(srh_seq)
## [>] sequence object created with TraMineR version 2.0-8
## [>] 2612 sequences in the data set, 1901 unique
## [>] min/max sequence length: 10/11
## [>] alphabet (state labels):
## 1=B1 (NotW)
## 2=B2 (Bad)
## 3=M (Average)
## 4=G1 (VeryW)
## 5=G2 (Well)
## [>] dimensionality of the sequence space: 44
## [>] colors: 1=#D7191C 2=#FDAE61 3=#FFFFBF 4=#A6D96A 5=#1A9641
## [>] symbol for missing state: *
## [>] symbol for void element: %
Object này có dạng 1 matrix hay dataframe, tức là bạn có thể thao tác với nó thoải mái như bất kì matrix hay dataframe nào khác, thí dụ đổi tên biến bằng chuỗi thời gian từ năm 1999 đến 2009
colnames(srh_seq)=c(1999:2009)
srh_seq%>%head()%>%knitr::kable()
G1 |
G1 |
G1 |
G1 |
G1 |
G1 |
G1 |
G2 |
G1 |
G1 |
G2 |
B1 |
G2 |
M |
G1 |
G1 |
G1 |
M |
M |
B1 |
B1 |
G2 |
G2 |
G2 |
G2 |
G2 |
G2 |
* |
G2 |
G2 |
G1 |
G2 |
G2 |
M |
M |
G2 |
G2 |
M |
G2 |
G2 |
G2 |
G2 |
M |
M |
* |
G1 |
G2 |
G1 |
G1 |
G2 |
G1 |
G2 |
M |
G2 |
G1 |
G1 |
G1 |
G1 |
G2 |
G1 |
* |
G2 |
G2 |
G2 |
G2 |
G2 |
Ta có thể tóm tắt xem có bao nhiêu bậc trạng thái trong dữ liệu này:
seqstatl(srh_seq)
## [1] "%" "*" "B1" "B2" "G1" "G2" "M"
alphabet(srh_seq)
## [1] "B1" "B2" "M" "G1" "G2"
Ta có thể khảo sát một số cá thể ngẫu nhiên trong dữ liệu dưới hình thức SPS:Định dạng SPS như đã giới thiệu bên trên, cho ta biết trình tự các trạng thái trong chuỗi cho mỗi cá thể, và thời gian cho mỗi trạng thái như vậy
print(srh_seq[1:5, ], format = "SPS")
## Sequence
## [1] (G1,7)-(G2,1)-(G1,2)-(G2,1)
## [2] (B1,1)-(G2,1)-(M,1)-(G1,3)-(M,2)-(B1,2)-(G2,1)
## [3] (G2,5)-(*,1)-(G2,2)-(G1,1)-(G2,2)
## [4] (M,2)-(G2,2)-(M,1)-(G2,4)-(M,2)
## [5] (*,1)-(G1,1)-(G2,1)-(G1,2)-(G2,1)-(G1,1)-(G2,1)-(M,1)-(G2,1)-(G1,1)
Tiếp theo, chúng ta sẽ vẽ một số biểu đồ, để khảo sát trực quan thông tin về chuỗi trạng thái trong dữ liệu
Biểu đồ thứ nhất mô tả tỉ lệ phân bố các trạng thái trong toàn bộ mẫu:nó có bản chất là 1 biểu đồ Stackedbar, xếp cạnh nhau
par(mfrow = c(1,2))
seqdplot(srh_seq,
main = "States distribution",
with.legend = F,border=NA)
seqlegend(srh_seq)

Ta có thể vẽ thủ công biểu đồ này bằng ggplot2:
pat<-seqstatd(SRH.seq)%>%.$Frequencies
colnames(pat)<-c(1:11)
patdf<-pat%>%as.tibble()%>%mutate(Status=rownames(pat))
longdf<-patdf%>%gather(`1`:`11`,key="Timepoint",value="Dist")
longdf$Timepoint=as.integer(longdf$Timepoint)
longdf%>%ggplot(aes(x=Timepoint,
y=Dist,fill=Status))+
geom_bar(stat="identity",position="fill",width=1,col="black")+
scale_fill_manual(values = pals::brewer.spectral(5))+theme_bw()

longdf%>%ggplot(aes(x=Timepoint,
y=Dist,fill=Status))+
geom_bar(stat="identity",position="fill",width=1,col="black")+
theme_bw()+coord_polar(theta="x")+
scale_fill_manual(values = pals::brewer.spectral(5))

longdf%>%ggplot(aes(x=Timepoint,
y=Dist,fill=Status))+
geom_bar(stat="identity",position="identity",width=1,col="black")+
theme_bw()+coord_polar(theta="y")+
scale_fill_manual(values = pals::brewer.spectral(5))

Biểu đồ này chỉ mới cung cấp thông tin về tỉ lệ của các trạng thái một cách tổng quát (cho quần thể), theo đó có thể thấy người ta có khuynh hướng tự đánh giá sức khỏe của mình một cách lạc quan vừa phải (mức độ G2 hoặc M).
Một dạng biểu đồ khác có bản chất là 1 heatmap, cho phép khảo sát trình tự diễn biến cho từng cá thể, nhưng nó chỉ có thể trình bày mỗi lần tối đa 10 cá thể:
Kết quả trên 10 người đầu tiên trong danh sách cho thấy đa số có trạng thái sức khỏe hằng năm dao động giữa G1 và G2, chỉ có 1 ngoại lệ là người số 2 có những thay đổi khá đột ngột từ B1 sang G2 và M.
par(mfrow = c(1,2))
seqiplot(srh_seq,
main = "Index plot (first 10 persons)",
with.legend = F)
seqlegend(srh_seq)

Dạng biểu đồ thứ ba, hữu dụng hơn cả, vì nó cho phép khảo sát khuynh hướng diễn tiến chung trong toàn bộ 2612 chuỗi trạng thái. Theo đó, khuynh hướng phổ biến nhất là một sự ổn định ở mức G1-G2
par(mfrow = c(1,2))
seqfplot(srh_seq,
main = "Sequence frequency",
with.legend = F,
pbarw = T)
seqlegend(srh_seq)

Ta còn có thể so sánh đặc tính phân bố các sự kiện/trạng thái giữa 2 phân nhóm, thí dụ giới tính:
seqmtplot(srh_seq, group = SRH$sex)

Và tương tự, so sánh khuynh hướng diễn tiến chung giữa 2 giới tính
seqiplot(srh_seq, group = SRH$sex, idxs = 0, border = NA,space = 0)

Khai thác sâu hơn tính chất chuỗi trạng thái
Trong bước tiếp theo, chúng ta bắt đầu khảo sát sâu hơn về diễn tiến của các chuỗi trạng thái trong dữ liệu: Mục tiêu là tính toán vài trị số thống kê và làm các phân tích xác suất.
Trị số thống kê đầu tiên có tên là Entropy (theo Shannon) cho mỗi chuỗi. Entropy đo lường tính chất “bất ổn định” của việc tiên lượng các trạng thái trong chuỗi. Nếu một chuỗi chỉ chứa toàn 1 trạng thái, entropy=0; entropy của một chuỗi có độ dài 12 thời điểm và có 4 trạng thái sẽ đạt cực đại = 1.386 nếu mỗi trạng thái xảy ra đúng 3 lần.
srh_seq%>%seqient()%>%head()
## Entropy
## 1 0.2945993
## 2 0.8530953
## 3 0.2019854
## 4 0.4281055
## 5 0.5861353
## 6 0.4181657
srh_seq%>%seqient()%>%apply(.,2,mean)
## Entropy
## 0.3714447
Kết quả trên trình bày giá trị Entropy của 6 người đầu tiên, và giá trị Entropy trung bình cho toàn bộ mẫu là 0.3714
Ghi chú là entropy không xét đến thứ tự thay đổi của các sự kiện/trạng thái, nhưng chỉ đếm số lần trạng thái đó xảy ra.
Một trị số khác là Turbulence (theo Elzinga, 2006) cũng mô tả tính bất định của chuỗi, nhưng chịu chi phối của trình tự các trạng thái:
srh_seq%>%seqient()%>%hist(col="gold",xlab = "Entropy")

srh_seq%>%seqST()%>%head()
## Turbulence
## 1 4.088201
## 2 7.705978
## 3 5.682061
## 4 6.108098
## 5 8.727920
## 6 5.625586
srh_seq%>%seqST()%>%hist(col="red",xlab = "Turbulence")

Ta có thể khảo sát tương quan giữa Entropy và Turbulence:
SRH%>%mutate(Entropy=seqient(srh_seq),
Turbulence=seqST(srh_seq))%>%
ggplot(aes(x=Turbulence,y=Entropy))+
geom_point(alpha=0.2)+
geom_smooth(col="red",se=F)+
theme_bw()

Và so sánh Entropy giữa 10 nhóm tuổi (năm sinh)
SRH%>%mutate(Entropy=seqient(srh_seq),
BYGroup=cut(SRH$birthy,b=10,
include.lowest = TRUE))%>%
ggplot(aes(x=BYGroup,y=Entropy))+
lvplot::geom_lv(col="black",
show.legend = F,
aes(fill=..LV..),
outlier.shape = 3)+
scale_fill_brewer(palette="Spectral")+
theme_bw()+coord_flip()

Biểu đồ này cho thấy Những người già và trung niên có sự bất định cao trong diễn tiến sức khỏe, biểu hiện bằng giá trị entropy cao, so với những người trẻ tuổi (sinh sau 1978).
Tiếp theo, chúng ta sẽ làm một phân tích xác suất đơn giản, nhằm khảo sát khả năng biến đổi từ một trạng thái sức khỏe xác định (thí dụ G1) ở thời điểm t sang một trạng thái khác ở thời điểm t+1 (bao gồm sự duy trì chính nó):
Phân tích này gồm 2 bước :
Đầu tiên ta sẽ tạo matrix xác suất cho từng cặp trạng thái:
pmat=seqtrate(srh_seq) %>% as.matrix()
pmat
## [-> B1] [-> B2] [-> M] [-> G1] [-> G2]
## [B1 ->] 0.156549521 0.0447284345 0.42172524 0.03514377 0.3418530
## [B2 ->] 0.268292683 0.2682926829 0.26829268 0.04878049 0.1463415
## [M ->] 0.043119848 0.0038046925 0.38237159 0.06055802 0.5101458
## [G1 ->] 0.002486679 0.0000000000 0.03996448 0.46642984 0.4911190
## [G2 ->] 0.006657617 0.0007756448 0.10425958 0.15364230 0.7346649
colnames(pmat)=c("B1","B2","M","G1","G2")
rownames(pmat)=c("B1","B2","M","G1","G2")
Kết quả này rất thú vị, và hợp logic, ta có thể diễn giải nó một cách phong phú, thí dụ: Một người khỏe mạnh và lạc quan (G1) có nhiều cơ may tiếp tục khỏe mạnh vào năm sau, một người bệnh tật (B1) vẫn có đến 42% đạt mức trung bình (M) vào năm tiếp theo, nhưng rất ít cơ may trở về mức khỏe mạnh (3.5%). …
Kết quả này có thể được chuyển thành một biểu đồ mạng (network)
library(ggraph)
cg=data.frame(row=rownames(pmat)[row(pmat)],
col=colnames(pmat)[col(pmat)],
Prob=pmat%>%as.vector())
names(cg)=c("from","to","Prob")
library(igraph)
graph<-graph_from_data_frame(cg)
ggraph(graph,circular=F,layout = 'lgl')+
geom_edge_fan(aes(col=Prob,label=round(Prob,2)),
show.legend = T,
arrow = arrow(length = unit(5, 'mm')),
alpha=0.5)+
geom_edge_loop(aes(col=Prob,label=round(Prob,2)),span=1,alpha=0.5)+
geom_node_label(aes(label = name))+
coord_fixed()+
scale_edge_color_gradient(low="blue",high="red")+
theme_graph()

Tiếp theo, ta có thể áp dụng phương pháp Clustering để phân cụm 200 cá thể chọn ngẫu nhiên trong mẫu dựa vào đặc tính của chuỗi trạng thái.
Đầu tiên, ta chọn ngẫu nhiên 200 cá thể, rồi dựng matrix giá trị distance theo phương pháp LCS (Longest Common Subsequence). Sau đó ta dùng phương pháp phân cụm của Ward.
Kết quả phân cụm có thể được trình bày bằng dendogram dưới 3 hình thức:
set.seed(123)
library(cluster)
library(factoextra)
srh_seq200<-seqdef(sample_n(SRH,200),5:15,
states=c("B1","B2","M","G1","G2"),
labels=c("NotW","Bad","Average","VeryW","Well"),
cpal=pals::brewer.rdylgn(5))
colnames(srh_seq200)=c(1999:2009)
dmat=seqdist(srh_seq200, method = "LCS",with.missing = T,norm=TRUE)
clusterward <- agnes(dmat, diss = TRUE, method = "ward")
fviz_dend(clusterward , k = 6, # Cut in 6 groups
cex = 0.5, # label size
k_colors = c("#24a3f2","#2edb9b","#243ff2","#f2a624","#a759f9", "#f95987"),
color_labels_by_k = TRUE, # color labels by groups
rect = TRUE, # Add rectangle around groups
rect_border = c("#24a3f2","#2edb9b","#243ff2","#f2a624", "#a759f9", "#f95987"),
rect_fill = TRUE)

fviz_dend(clusterward , k = 6, # Cut in 6 groups
cex = 0.5, # label size
k_colors = c("#24a3f2","#2edb9b","#243ff2","#f2a624","#a759f9", "#f95987"),
color_labels_by_k = TRUE, # color labels by groups
type = "circular")

library("igraph")
fviz_dend(clusterward, k = 6, # Cut in 6 groups
k_colors = c("#24a3f2","#2edb9b","#243ff2","#f2a624","#a759f9", "#f95987"),
type = "phylogenic", repel = TRUE,
phylo_layout = "layout.gem")

Lưu ý: Ở đây ta giả định phân dữ liệu thành 6 cụm.
Ta có thể giảm số lượng còn 3 cluster, những biểu đồ sau đây cho phép so sánh đặc tính trình tự chuỗi trạng thái giữa 3 cluster:
cluster3 <- cutree(clusterward, k = 3)
cluster3 <- factor(cluster3, labels = c("Cluster 1", "Cluster 2", "Cluster 3"))
seqfplot(srh_seq200, group = cluster3, pbarw = T)

seqmtplot(srh_seq200, group = cluster3)

Kết quả này cho thấy Cluster 1 gồm những người khỏe mạnh nhất (G1), diễn tiến sức khỏe của họ trong 10 năm dao động giữa G1 và G2 với ít nguy cơ giảm xuống mức B1 hay B2; Cluster 2 gồm những người ít lạc quan hơn nhưng có tình trạng sức khỏe rất tốt và ổn định với sữ duy trì mức G2; Cuối cùng, cluster 3 gồm những người có trạng thái sức khỏe không ổn định và dao động quanh mức trung bình /xấu, với nguy cơ cao xảy ra trạng thái B1, thậm chí B2
Kết luận
Bài thực hành ngắn hôm nay đã giới thiệu với các bạn một ý tưởng mới có thể áp dụng cho nghiên cứu lâm sàng/dịch tễ với thiết kế trường diễn/theo dõi kéo dài và kết quả là một biến rời rạc nhiều giá trị. Ý tưởng này độc đáo hơn so với nghiên cứu theo dõi biến định lượng theo thời gian hoặc survival analysis,với nhiều ứng dụng tiềm năng. Nhi cũng giới thiệu sơ lược về một công cụ trong R cho phép khảo sát chuỗi sự kiện/trạng thái. Lưu ý rằng trong bài chúng ta chỉ mới khảo sát khoảng 30% những tính năng mà package TraMineR cung cấp. Ngoài ra, một package khác có tên là PST còn cho phép dựng mô hình cây tiên đoán chuỗi sự kiện mà Nhi sẽ giới thiệu trong một dịp khác.
Chúc các bạn thực hành vui, và áp dụng được ý tưởng mới này vào nghiên cứu của riêng mình.
LS0tDQp0aXRsZTogIlBow6JuIHTDrWNoIGNodeG7l2kgdHLhuqFuZyB0aMOhaSINCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjI4IFRow6FuZyA2IDIwMTgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiFbXShTZXFBLnBuZykNCg0KIyBHaeG7m2kgdGhp4buHdQ0KDQpUcm9uZyBt4buZdCBjdeG7mWMgdGjEg20gZMOyIHRoxrDhu51uZyBuacOqbiB2w6Aga8OpbyBkw6BpIHThu6sgMTk5OS0yMDA5IGPhu6dhIHThu5UgY2jhu6ljIFN3aXNzIEhvdXNlaG9sZCBQYW5lbCwgMjYxMiBuZ8aw4budaSBkw6JuIFRo4buleSBT4bu5IMSRw6MgdOG7sSDEkcOhbmggZ2nDoSBz4bupYyBraOG7j2UgY+G7p2EgYuG6o24gdGjDom4gdGhlbyB0aGFuZyDEkWnhu4NtIGfhu5NtIDUgbeG7qWMgxJHhu5k6IEcxPVLhuqV0IGto4buPZSBt4bqhbmggKFZlcnkgd2VsbCksIEcyPUto4buPZSAoV2VsbCksIE09VHJ1bmcgYsOsbmggKEF2ZXJhZ2UpLCBCMT1LaMO0bmcgdOG7kXQgdsOgIEIyPVjhuqV1IChCYWQpLiANCg0KTuG7mWkgZHVuZyBj4bunYSBk4buvIGxp4buHdSBuaMawIHNhdTogDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KFBTVCkNCg0KZGF0YShTUkgpDQoNClNSSCU+JWhlYWQoKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KVGhp4bq/dCBr4bq/IG5naGnDqm4gY+G7qXUgbsOgeSBo4bq/dCBz4bupYyB0aMO6IHbhu4sgOiDhu54gdGjhu51pIMSRaeG7g20ga2jhu59pIMSR4bqndSwgY2jGsGEgY8OzIG3hu5l0IGdp4bqjIHRodXnhur90IHjDoWMgxJHhu4tuaCBuw6BvOiBN4bulYyB0acOqdSBjw7MgdGjhu4MgbMOgIGtoYWkgcGjDoSBk4buvIGxp4buHdTogdMOsbSByYSBt4buZdCBxdXkgbHXhuq10IGRp4buFbiB0aeG6v24gKHBhdHRlcm4pIGPhu6dhIHPhu6ljIGto4buPZSBj4bunYSBxdeG6p24gdGjhu4MgdGhlbyB0aOG7nWkgZ2lhbi4gVuG7m2kgcXV5IGx14bqtdCBuw6B5LCB0YSBoeSB24buNbmcgY8OzIHRo4buDIHRpw6puIGzGsOG7o25nIMSRxrDhu6NjIHPhu6ljIGto4buPZSBj4bunYSBt4buZdCBjw6EgdGjhu4MgdOG6oWkgbeG7mXQgdGjhu51pIMSRaeG7g20gYuG6pXQga8OsIG7hur91IGJp4bq/dCB0aMO0bmcgdGluIHbhu4EgbmfGsOG7nWkgbsOgeSB0cm9uZyBxdcOhIGto4bupLiANCg0Kw50gdMaw4bufbmcga2jhuqNvIHPDoXQgY2h14buXaSB0cuG6oW5nIHRow6FpL3Phu7Ega2nhu4duIGPDsyB0aOG7gyDDoXAgZOG7pW5nIHRyb25nIG5oaeG7gXUgbMSpbmggduG7sWMsIHRow60gZOG7pTogDQoNCjEpIFRoxrDGoW5nIG3huqFpOiBN4buZdCBj4butYSBow6BuZyDEkWnhu4duIHRob+G6oWkgbXXhu5FuIGto4bqjbyBzw6F0IGtodXluaCBoxrDhu5tuZyBs4buxYSBjaOG7jW4gdGhheSDEkeG7lWkgc21hcnRwaG9uZSBj4bunYSBraMOhY2ggaMOgbmcgdGhlbyB0aOG7nWkgZ2lhbiwgdHJvbmcgc+G7kSBjw6FjIHRoxrDGoW5nIGhp4buHdSwgdOG7qyDEkcOzIGPDsyB0aOG7gyB0acOqbiBsxrDhu6NuZyB4w6FjIHN14bqldCBt4buZdCBuZ8aw4budaSDEkWFuZyBz4butIGThu6VuZyBtw6F5IFNhbXN1bmcgc+G6vSBjaHV54buDbiBzYW5nIGTDuW5nIElQaG9uZSBob+G6t2MgdGnhur9wIHThu6VjIHRydW5nIHRow6BuaCB24bubaSB0aMawxqFuZyBoaeG7h3UgY8WpLg0KDQoyKSBZIGjhu41jIGzDom0gc8Ogbmc6IFRoZW8gZMO1aSBkaeG7hW4gdGnhur9uIGPhu6dhIG3hu5l0IGLhu4duaCBsw70gdGhlbyB0aOG7nWkgZ2lhbiwgdGjDrSBk4bulIGzhu5kgdHLDrG5oIGRp4buFbiB0aeG6v24gY+G7p2EgYuG7h25oIHN1eSB0aW0gKHRoZW8gTllIQSksIGLhu4duaCBwaOG7lWkgdOG6r2Mgbmdo4bq9biBt4bqhbiB0w61uaCwgaG/hurdjIHRy4bqhbmcgdGjDoWkgZGluaCBkxrDhu6FuZyAoYsOsbmggdGjGsOG7nW5nL3Ro4burYSBjw6JuL2LDqW8gcGjDrC4uLikgOyBob+G6t2Mgc+G7sSB0aGF5IMSR4buVaSBwaMawxqFuZyBwaMOhcCDEkWnhu4F1IHRy4buLIChjaHV54buDbiB04burIHRodeG7kWMgZMOjbiBwaOG6vyBxdeG6o24gc2FuZyBjb3J0aWNvaWQsIHThu6sga2jDoW5nIHNpbmggdGjGsOG7nW5nIHNhbmcga2jDoW5nIHNpbmggdMOibiB0aeG6v24gaMahbiwgZ2nhu69hIGPDoWMgbG/huqFpIHRodeG7kWMgZ2nhuqNtIMSRYXUuLi4pDQoNCjMpIFjDoyBo4buZaSBo4buNYzogVGhlbyBkw7VpIGRp4buFbiB0aeG6v24gY+G7p2EgdMOsbmggdHLhuqFuZyBHaWEgxJHDrG5oICjEkOG7mWMgdGjDom4sIGvhur90IGjDtG4sIGPDsyBjb24uLi4pLCBo4buNYyB24bqlbi9uZ2jhu4EgbmdoaeG7h3AgKHjDoWMgc3XhuqV0IHbDoG8gxJHhuqFpIGjhu41jLCB2w6AgaOG7jWMgdGnhur9wIGzDqm4gY2FvIGjhu41jIGhheSDEkWkgbMOgbSwgbmd1eSBjxqEgdGjhuqV0IG5naGnhu4dwLCBz4buxIGNodXnhu4NuIMSR4buVaSBuZ2jhu4EsIGPDtG5nIHZp4buHYyksIHTDtG4gZ2nDoW8sIC4uLg0KDQpW4bqlbiDEkeG7gSBuw6B5IGhvw6BuIHRvw6BuIG3hu5tpIGzhuqEgxJHhu5FpIHbhu5tpIG5oaeG7gXUgYuG6oW4sIHbDoCB5w6p1IGPhuqd1IG3hu5l0IHBoxrDGoW5nIHBow6FwIHRo4buRbmcga8OqIMSR4bq3YyBiaeG7h3QgY8OzIHTDqm4gbMOgICJQaMOibiB0w61jaCBjaHXhu5dpIHRy4bqhbmcgdGjDoWkiLCBtw6AgbeG7pWMgdGnDqnUgbMOgIGtoYWkgdGjDoWMgdGjDtG5nIHRpbiB04burIGThu68gbGnhu4d1IHRyxrDhu51uZyBkaeG7hW4gKGxvbmdpdHVkaW5hbCkgduG7m2kgb3V0Y29tZSBsw6AgbeG7mXQgYmnhur9uIHLhu51pIHLhuqFjIG5oaeG7gXUgYuG6rWMgZ2nDoSB0cuG7iyAodHLhuqFuZyB0aMOhaSBoYXkgc+G7sSBraeG7h24pLg0KDQpCw6BpIHRo4buxYyBow6BuaCBow7RtIG5heSwgTmhpIHPhur0gZ2nhu5tpIHRoaeG7h3UgduG7m2kgY8OhYyBi4bqhbiBjw7RuZyBj4bulIGNodXnDqm4gZOG7pW5nIGNobyBsb+G6oWkgbmdoacOqbiBj4bupdSBuw6B5LCDEkcOzIGzDoCBwYWNrYWdlIFRSYU1pbmVSLg0KDQojIEPhuqV1IHRyw7pjIGThu68gbGnhu4d1IGNodeG7l2kgDQoNCsSQ4bqndSB0acOqbiwgY2jDum5nIHRhIGPhuqduIGLDoG4gduG7gSBj4bqldSB0csO6YyBj4bunYSBk4buvIGxp4buHdSBjaG8gdGhp4bq/dCBr4bq/IG5naGnDqm4gY+G7qXUgdGhlbyBkw7VpIGNodeG7l2kgdHLhuqFuZyB0aMOhaS4gVuG6pW4gxJHhu4EgbMOgIGThu68gbGnhu4d1IGxv4bqhaSBuw6B5IGPDsyB0aOG7gyDEkcaw4bujYyB04bqhbyByYSBkxrDhu5tpIG5oaeG7gXUgZOG6oW5nIHbDoCBjaMO6bmcga2jDoWMgbmhhdSB24buBIGPhuqV1IHRyw7pjLiBOZ2/DoGkgY8OhYyB0aHXhu5ljIHTDrW5oIG5oxrAgR2nhu5tpIHTDrW5oLCBOxINtIHNpbmgsIC4uLiAsIGThu68gbGnhu4d1IGNodeG7l2kgY8OzIDMgdGjDoG5oIHBo4bqnbiBjaMOtbmg6IEJp4bq/biDEkeG7i25oIGRhbmggY8OhIHRo4buDICwgVGjhu51pIGdpYW4vVGjhu51pIMSRaeG7g20gdsOgIHRy4bqhbmcgdGjDoWkuIA0KDQoxKSBTVFM6IEThuqFuZyB0aOG7qSBuaOG6pXQgdMOqbiBsw6AgU3RhdGUtc2VxdWVuY2UsIGPDsyBj4bqldSB0csO6YyBXaWRlLCBt4buXaSBow6BuZyBsw6AgbeG7mXQgY8OhIHRo4buDIGksIGPhu5l0IMSR4bqndSB0acOqbiDEkeG7gyDEkeG7i25oIGRhbmgsIGPDoWMgdGjhu51pIMSRaeG7g20gdsOgIHRy4bqhbmcgdGjDoWkgxJHGsOG7o2MgdHLDrG5oIGLDoHkgYuG6sW5nIG5oaeG7gXUgY+G7mXQgbuG7kWkgdGnhur9wIG5oYXUsIG3hu5dpIGPhu5l0IGzDoCBt4buZdCB0aOG7nWkgxJFp4buDbSB0IHbDoCBt4buXaSDDtCBuaOG6rW4gMSBnacOhIHRy4buLIHRy4bqhbmcgdGjDoWkgKGlqKQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmRhdGFfZnJhbWUoSWQ9YygxOjUpLFQxPWMoIkEiLCJBIiwiQiIsIkMiLCJEIiksDQogICAgICAgICAgIFQyPWMoIkEiLCJCIiwiQyIsIkQiLCJEIiksDQogICAgICAgICAgIFQzPWMoIkEiLCJCIiwiQiIsIkEiLCJDIikNCiAgICAgICAgICAgKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KMikgU1BTOiBTdGF0ZS1wZXJtYW5lbmNlLiBE4bqhbmcgZOG7ryBsaeG7h3UgbsOgeSBjxaluZyBjw7MgY+G6pXUgdHLDumMgd2lkZSwgbeG7l2kgaMOgbmcgY8WpbmcgdHLDrG5oIGLDoHkgMSBjw6EgdGjhu4MsIG5oxrBuZyBt4buXaSBj4buZdCBiaeG7g3UgdGjhu4sgY2hvIG3hu5l0IGLhuq1jIHRy4bqhbmcgdGjDoWkgdsOgIG5o4bqtbiBnacOhIHRy4buLIGfhu5NtIDIgdGjDtG5nIHRpbiAoVMOqbiB0cuG6oW5nIHRow6FpL3Ro4budaSBnaWFuIGvDqW8gZMOgaSBjaG8gdHLhuqFuZyB0aMOhaSDEkcOzKS4gDQoNCsavdSDEkWnhu4NtIGPhu6dhIMSR4buLbmggZOG6oW5nIG7DoHkgbMOgIGNobyBwaMOpcCBraOG6o28gc8OhdCB0aMOqbSB0aOG7nWkgZ2lhbiBuaMawIG3hu5l0IGJp4bq/biBsacOqbiB04bulYywgbmjGsG5nIGtow7MgdGhhbyB0w6FjIHbDoCBxdeG6o24gbMO9Lg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmRhdGFfZnJhbWUoSWQ9YygxOjUpLFN0YXRlMT1jKCJBLzMiLE5BLCJCLzQiLCJDLzIiLCJELzEiKSwNCiAgICAgICAgICAgU3RhdGUyPWMoIkMvMiIsIkIvMyIsIkEvNCIsIkQvMSIsTkEpLA0KICAgICAgICAgICBTdGF0ZTM9YygiRC8zIiwiQy8xIixOQSwiRC8zIiwiQS80IikNCiAgICAgICAgICAgKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KMykgVFNFOiBUaW1lLXN0YW1wZWQgZXZlbnQuIFRyw6FpIHbhu5tpIDIgZOG6oW5nIHRyw6puLCBUU0UgY8OzIGPhuqV1IHRyw7pjIExvbmcsIGfhu5NtIDMgY+G7mXQ6IDEgY+G7mXQgxJHhu4tuaCBkYW5oLCAxIGPhu5l0IMSR4buDIMSRbyB0aOG7nWkgZ2lhbiBuaMawIGNvbiBz4buRIHRo4buxYyBz4buxLCAxIGPhu5l0IGNo4buJIHPhu7Ega2nhu4duLiBD4bqldSB0csO6YyBk4buvIGxp4buHdSBuw6B5IHLhuqV0IGThu4UgdGhhbyB0w6FjLCBuaOG6rXAgbGnhu4d1IHbDoCBjaG8gcGjDqXAga2jhuqNvIHPDoXQgdGjhu51pIGdpYW4gIG5oxrAgbeG7mXQgYmnhur9uIMSR4buLbmggbMaw4bujbmcgKHRow60gZOG7pSB0deG7lWksIG5nw6B5LyB0aMOhbmcpLCBjxaluZyBuaMawIHRoZW8gZMO1aSBjaHXhu5dpIHPhu7Ega2nhu4duLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmRhdGFfZnJhbWUoSWQ9YygxLDEsMSwyLDIsMiwzLDMsMyksDQogICAgICAgICAgIEFnZT1jKDI0LDI1LDI2LDIyLDIzLDI0LDMxLDMyLDMzKSwNCiAgICAgICAgICAgRXZlbnQ9YygiQSIsIkIiLCJDIiwiQSIsIkMiLCJEIiwiQiIsIkMiLCJEIikNCiAgICAgICAgICAgKSU+JWtuaXRyOjprYWJsZSgpDQpgYGANCg0KNCkgU1BFTEw6IMSQw6J5IGzDoCBt4buZdCBk4bqhbmcgbeG7nyBy4buZbmcgdOG7qyBUU0UsIG7DsyBjxaluZyBjw7MgY+G6pXUgdHLDumMgTG9uZywgZ+G7k20gNCBj4buZdCwgbeG7mXQgY+G7mXQgxJHhu4tuaCBkYW5oLCBt4buZdCBj4buZdCBjaOG7iSBz4buxIGtp4buHbiwgMSBj4buZdCBjaOG7iSB0aOG7nWkgxJFp4buDbSBwaMOhdCBzaW5oIHPhu7Ega2nhu4duIHbDoCAxIGPhu5l0IGNo4buJIHRo4budaSBnaWFuIGNo4bqlbSBk4bupdCBz4buxIGtp4buHbi4gxJDDonkgbMOgIMSR4buLbmggZOG6oW5nIGThu68gbGnhu4d1IGxpbmggaG/huqF0IG5o4bqldCwgbsOzIGNobyBwaMOpcCBraOG6o28gc8OhdCBj4bqjIG5o4buvbmcgc+G7sSBraeG7h24gdHLDuW5nIGjhu6NwLCB44bqjeSByYSDEkeG7k25nIHRo4budaSwgY8WpbmcgbmjGsCBraOG6o28gc8OhdCDEkcaw4bujYyBj4bqjIGtob+G6o25nIHRo4budaSBnaWFuIHbDoCB0aOG7nWkgxJFp4buDbS5N4buZdCBiaeG6v24gcGjhu6UgRHVyYXRpb24gY8OzIHRo4buDIMSRxrDhu6NjIHThuqFvIHJhIMSR4buDIMSRbyBraG/huqNuZyB0aOG7nWkgZ2lhbiBz4buxIGtp4buHbiBkaeG7hW4gcmEuIEPhuqV1IHRyw7pjIGThu68gbGnhu4d1IG7DoHkgY8OzIHRo4buDIMSRxrDhu6NjIHThuqFvIHJhIHThu7EgxJHhu5luZyB04burIGThu68gbGnhu4d1IGNodeG7l2kgdGjhu51pIGdpYW4gYuG6sW5nIG3hu5l0IGjDoG07IGtoaSBiaeG6v3QgZ2nDoSB0cuG7iyBzdGFydCBwb2ludCB2w6AgZW5kIHBvaW50Lg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmRhdGFfZnJhbWUoSWQ9YygxLDEsMSwyLDIsMiwzLDMsMyksDQogICAgICAgICAgIEZyb209YygxOCwyMywyOCwxNiwyNiwzMywyMSwzNSw0OCksDQogICAgICAgICAgIFRvPWMoMjIsMjcsNDAsMjUsMzIsNDgsMzQsNDcsNTIpLA0KICAgICAgICAgICBFdmVudD1jKCJBIiwiQiIsIkMiLCJBIiwiQyIsIkQiLCJCIiwiQyIsIkQiKSwNCiAgICAgICAgICAgRHVyYXRpb249YyhUby1Gcm9tKQ0KICAgICAgICAgICApJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpUcuG7nyBs4bqhaSB24bubaSB0aMOtIGThu6UgdHJvbmcgYsOgaSwgdGEgY8OzIHRo4buDIHRo4bqleSBuw7MgY8OzIGThuqFuZyBTVFMuDQoNClBhY2thZ2UgVHJhTWluZVIgY2hvIHBow6lwIGNodXnhu4NuIMSR4buVaSB0aG/huqNpIG3DoWkgZ2nhu69hIGPDoWMgxJHhu4tuaCBk4bqhbmcgZOG7ryBsaeG7h3UsIHRow60gZOG7pSB0YSBjw7MgdGjhu4MgY2h1eeG7g24gdOG7qyBTVFMgc2FuZyBTUEVMTDoNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkoVHJhTWluZVIpDQoNCnNyaF9zZXE9c2VxZGVmKFNSSFssNToxNV0pDQoNCnNlcWZvcm1hdChzcmhfc2VxLCANCiAgICAgICAgICAgIGZyb20gPSAiU1RTIiwgdG8gPSAiU1BFTEwiLA0KICAgICAgICAgICAgICBwcm9jZXNzPUYsDQogICAgICAgICAgICAgICAgd2l0aC5taXNzaW5nPVRSVUUpJT4laGVhZCgpJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpIYXkgY2h1eeG7g24gdOG7qyBTVFMgc2FuZyBTUFM6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kc2VxZm9ybWF0KHNyaF9zZXEsIA0KICAgICAgICAgICAgZnJvbSA9ICJTVFMiLCB0byA9ICJTUFMiLA0KICAgICAgICAgICAgICBwcm9jZXNzPUYsDQogICAgICAgICAgICAgICAgd2l0aC5taXNzaW5nPVRSVUUpJT4laGVhZCgpJT4la25pdHI6OmthYmxlKCkNCmBgYA0KDQpDxaluZyBuaMawIHThu6sgU1RTIHNhbmcgVFNFDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdHRyYW5zIDwtIHNlcWV0bShzcmhfc2VxLCBtZXRob2Q9J3RyYW5zaXRpb24nKQ0KDQpzZXFmb3JtYXQoc3JoX3NlcSwgDQogICAgICAgICAgICBmcm9tID0gIlNUUyIsIHRvID0gIlRTRSIsdGV2ZW50PXR0cmFucywNCiAgICAgICAgICAgICAgcHJvY2Vzcz1GLA0KICAgICAgICAgICAgICAgIHdpdGgubWlzc2luZz1UUlVFKSU+JWhlYWQoKSU+JWtuaXRyOjprYWJsZSgpDQoNCmBgYA0KDQpUcsOqbiB0aOG7sWMgdOG6vywgZG8gMiDEkeG7i25oIGThuqFuZyBTUEVMTCB2w6AgVFNFIGzDoCBk4buFIHF14bqjbiBsw70gbmjhuqV0LCBuw6puIGNow7puZyB0YSB0aMaw4budbmcgbmjhuq1wIGxp4buHdSBkxrDhu5tpIGThuqFuZyBuw6B5LCBuaMawbmcgxJHhu4tuaCBk4bqhbmcgU1RTIGzhuqFpIGzDoCBt4bq3YyDEkeG7i25oIGNobyBuaGnhu4F1IGjDoG0gdHJvbmcgcGFja2FnZSwgbsOqbiB0YSBwaOG6o2kgY2h1eeG7g24gZOG6oW5nIG5nxrDhu6NjIHThu6sgU1BFTEwgc2FuZyBTVFMgaGF5IHThu6sgVFNFIHNhbmcgU1RTLiDEkOG7gyBsw6BtIHZp4buHYyBuw6B5LCB0YSBjaOG7iSBj4bqnbiBraGFpIGLDoW8gMyBiaeG6v246IGlkLGJlZ2luL2VuZChjaG8gU1BFTEwpIGhheSB0aW1lIChjaG8gVFNFKSB2w6AgZXZlbnQvc3RhdHVzLg0KDQojIFRo4buRbmcga8OqIG3DtCB04bqjDQoNCsSQ4bqndSB0acOqbiwgdOG7qyBk4buvIGxp4buHdSBTUkgsIHRhIHBo4bqjaSB04bqhbyByYSBjaHXhu5dpIHRy4bqhbmcgdGjDoWkvc+G7sSBraeG7h24sIMSRw6J5IHPhur0gbMOgIG5ndXnDqm4gbGnhu4d1IGNobyB04bqldCBj4bqjIG3hu41pIHF1w6EgdHLDrG5oIHBow6JuIHTDrWNoIHRp4bq/cCB0aGVvIG7Dqm4gYsaw4bubYyBuw6B5IHLhuqV0IHF1YW4gdHLhu41uZzogDQoNCkLDqm4gdHJvbmcgb2JqZWN0IGNodeG7l2kgbsOgeSwgdGEgY8OzIHRo4buDIHjDoWMgxJHhu4tuaCB0aMOqbTogTmjDo24gdMOqbiBn4buNaSBjaG8gbeG7l2kgc+G7sSBraeG7h24vdHLhuqFuZyB0aMOhaSwgdGjDrSBk4bulIEIxLEIyLE0sRzEsRzIgKHRoZW8gdGjhu6kgdOG7sSBBQkMpLCB2w6AgcGjhu5UgbcOgdSBkw7luZyDEkeG7gyB24bq9IGJp4buDdSDEkeG7kyBzYXUgbsOgeToNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzcmhfc2VxPC1zZXFkZWYoU1JILDU6MTUsDQogICAgICAgICAgICAgICAgc3RhdGVzPWMoIkIxIiwiQjIiLCJNIiwiRzEiLCJHMiIpLA0KICAgICAgICAgICAgICAgIGxhYmVscz1jKCJOb3RXIiwiQmFkIiwiQXZlcmFnZSIsIlZlcnlXIiwiV2VsbCIpLA0KICAgICAgICAgICAgICAgIGNwYWw9cGFsczo6YnJld2VyLnJkeWxnbig1KSkNCg0Kc3VtbWFyeShzcmhfc2VxKQ0KYGBgDQoNCk9iamVjdCBuw6B5IGPDsyBk4bqhbmcgMSBtYXRyaXggaGF5IGRhdGFmcmFtZSwgdOG7qWMgbMOgIGLhuqFuIGPDsyB0aOG7gyB0aGFvIHTDoWMgduG7m2kgbsOzIHRob+G6o2kgbcOhaSBuaMawIGLhuqV0IGvDrCBtYXRyaXggaGF5IGRhdGFmcmFtZSBuw6BvIGtow6FjLCB0aMOtIGThu6UgxJHhu5VpIHTDqm4gYmnhur9uIGLhurFuZyBjaHXhu5dpIHRo4budaSBnaWFuIHThu6sgbsSDbSAxOTk5IMSR4bq/biAyMDA5DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KY29sbmFtZXMoc3JoX3NlcSk9YygxOTk5OjIwMDkpDQoNCnNyaF9zZXElPiVoZWFkKCklPiVrbml0cjo6a2FibGUoKQ0KYGBgDQoNClRhIGPDsyB0aOG7gyB0w7NtIHThuq90IHhlbSBjw7MgYmFvIG5oacOqdSBi4bqtYyB0cuG6oW5nIHRow6FpIHRyb25nIGThu68gbGnhu4d1IG7DoHk6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kc2Vxc3RhdGwoc3JoX3NlcSkNCg0KYWxwaGFiZXQoc3JoX3NlcSkNCmBgYA0KDQpUYSBjw7MgdGjhu4Mga2jhuqNvIHPDoXQgbeG7mXQgc+G7kSBjw6EgdGjhu4Mgbmfhuqt1IG5oacOqbiB0cm9uZyBk4buvIGxp4buHdSBkxrDhu5tpIGjDrG5oIHRo4bupYyBTUFM6xJDhu4tuaCBk4bqhbmcgU1BTIG5oxrAgxJHDoyBnaeG7m2kgdGhp4buHdSBiw6puIHRyw6puLCBjaG8gdGEgYmnhur90IHRyw6xuaCB04buxIGPDoWMgdHLhuqFuZyB0aMOhaSB0cm9uZyBjaHXhu5dpIGNobyBt4buXaSBjw6EgdGjhu4MsIHbDoCB0aOG7nWkgZ2lhbiBjaG8gbeG7l2kgdHLhuqFuZyB0aMOhaSBuaMawIHbhuq15DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcHJpbnQoc3JoX3NlcVsxOjUsIF0sIGZvcm1hdCA9ICJTUFMiKQ0KYGBgDQoNClRp4bq/cCB0aGVvLCBjaMO6bmcgdGEgc+G6vSB24bq9IG3hu5l0IHPhu5EgYmnhu4N1IMSR4buTLCDEkeG7gyBraOG6o28gc8OhdCB0cuG7sWMgcXVhbiB0aMO0bmcgdGluIHbhu4EgY2h14buXaSB0cuG6oW5nIHRow6FpIHRyb25nIGThu68gbGnhu4d1DQoNCkJp4buDdSDEkeG7kyB0aOG7qSBuaOG6pXQgbcO0IHThuqMgdOG7iSBs4buHIHBow6JuIGLhu5EgY8OhYyB0cuG6oW5nIHRow6FpIHRyb25nIHRvw6BuIGLhu5kgbeG6q3U6bsOzIGPDsyBi4bqjbiBjaOG6pXQgbMOgIDEgYmnhu4N1IMSR4buTIFN0YWNrZWRiYXIsIHjhur9wIGPhuqFuaCBuaGF1DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpwYXIobWZyb3cgPSBjKDEsMikpDQoNCnNlcWRwbG90KHNyaF9zZXEsIA0KICAgICAgICAgbWFpbiA9ICJTdGF0ZXMgZGlzdHJpYnV0aW9uIiwNCiAgICAgICAgIHdpdGgubGVnZW5kID0gRixib3JkZXI9TkEpDQoNCnNlcWxlZ2VuZChzcmhfc2VxKQ0KYGBgDQoNClRhIGPDsyB0aOG7gyB24bq9IHRo4bunIGPDtG5nIGJp4buDdSDEkeG7kyBuw6B5IGLhurFuZyBnZ3Bsb3QyOg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnBhdDwtc2Vxc3RhdGQoU1JILnNlcSklPiUuJEZyZXF1ZW5jaWVzDQoNCmNvbG5hbWVzKHBhdCk8LWMoMToxMSkNCg0KcGF0ZGY8LXBhdCU+JWFzLnRpYmJsZSgpJT4lbXV0YXRlKFN0YXR1cz1yb3duYW1lcyhwYXQpKQ0KDQpsb25nZGY8LXBhdGRmJT4lZ2F0aGVyKGAxYDpgMTFgLGtleT0iVGltZXBvaW50Iix2YWx1ZT0iRGlzdCIpDQoNCmxvbmdkZiRUaW1lcG9pbnQ9YXMuaW50ZWdlcihsb25nZGYkVGltZXBvaW50KQ0KDQpsb25nZGYlPiVnZ3Bsb3QoYWVzKHg9VGltZXBvaW50LA0KICAgICAgICAgICAgICAgICAgICB5PURpc3QsZmlsbD1TdGF0dXMpKSsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uPSJmaWxsIix3aWR0aD0xLGNvbD0iYmxhY2siKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsczo6YnJld2VyLnNwZWN0cmFsKDUpKSt0aGVtZV9idygpDQoNCmxvbmdkZiU+JWdncGxvdChhZXMoeD1UaW1lcG9pbnQsDQogICAgICAgICAgICAgICAgICAgIHk9RGlzdCxmaWxsPVN0YXR1cykpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIscG9zaXRpb249ImZpbGwiLHdpZHRoPTEsY29sPSJibGFjayIpKw0KICB0aGVtZV9idygpK2Nvb3JkX3BvbGFyKHRoZXRhPSJ4IikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHM6OmJyZXdlci5zcGVjdHJhbCg1KSkNCg0KbG9uZ2RmJT4lZ2dwbG90KGFlcyh4PVRpbWVwb2ludCwNCiAgICAgICAgICAgICAgICAgICAgeT1EaXN0LGZpbGw9U3RhdHVzKSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iixwb3NpdGlvbj0iaWRlbnRpdHkiLHdpZHRoPTEsY29sPSJibGFjayIpKw0KICB0aGVtZV9idygpK2Nvb3JkX3BvbGFyKHRoZXRhPSJ5IikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbHM6OmJyZXdlci5zcGVjdHJhbCg1KSkNCmBgYA0KDQpCaeG7g3UgxJHhu5MgbsOgeSBjaOG7iSBt4bubaSBjdW5nIGPhuqVwIHRow7RuZyB0aW4gduG7gSB04buJIGzhu4cgY+G7p2EgY8OhYyB0cuG6oW5nIHRow6FpIG3hu5l0IGPDoWNoIHThu5VuZyBxdcOhdCAoY2hvIHF14bqnbiB0aOG7gyksIHRoZW8gxJHDsyBjw7MgdGjhu4MgdGjhuqV5IG5nxrDhu51pIHRhIGPDsyBraHV5bmggaMaw4bubbmcgdOG7sSDEkcOhbmggZ2nDoSBz4bupYyBraOG7j2UgY+G7p2EgbcOsbmggbeG7mXQgY8OhY2ggbOG6oWMgcXVhbiB24burYSBwaOG6o2kgKG3hu6ljIMSR4buZIEcyIGhv4bq3YyBNKS4NCg0KTeG7mXQgZOG6oW5nIGJp4buDdSDEkeG7kyBraMOhYyBjw7MgYuG6o24gY2jhuqV0IGzDoCAxIGhlYXRtYXAsIGNobyBwaMOpcCBraOG6o28gc8OhdCB0csOsbmggdOG7sSBkaeG7hW4gYmnhur9uIGNobyB04burbmcgY8OhIHRo4buDLCBuaMawbmcgbsOzIGNo4buJIGPDsyB0aOG7gyB0csOsbmggYsOgeSBt4buXaSBs4bqnbiB04buRaSDEkWEgMTAgY8OhIHRo4buDOiANCg0KS+G6v3QgcXXhuqMgdHLDqm4gMTAgbmfGsOG7nWkgxJHhuqd1IHRpw6puIHRyb25nIGRhbmggc8OhY2ggY2hvIHRo4bqleSDEkWEgc+G7kSBjw7MgdHLhuqFuZyB0aMOhaSBz4bupYyBraOG7j2UgaOG6sW5nIG7Eg20gZGFvIMSR4buZbmcgZ2nhu69hIEcxIHbDoCBHMiwgY2jhu4kgY8OzIDEgbmdv4bqhaSBs4buHIGzDoCBuZ8aw4budaSBz4buRIDIgY8OzIG5o4buvbmcgdGhheSDEkeG7lWkga2jDoSDEkeG7mXQgbmfhu5l0IHThu6sgQjEgc2FuZyBHMiB2w6AgTS4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCnBhcihtZnJvdyA9IGMoMSwyKSkNCg0Kc2VxaXBsb3Qoc3JoX3NlcSwgDQogICAgICAgICBtYWluID0gIkluZGV4IHBsb3QgKGZpcnN0IDEwIHBlcnNvbnMpIiwNCiAgICAgICAgIHdpdGgubGVnZW5kID0gRikNCg0Kc2VxbGVnZW5kKHNyaF9zZXEpDQpgYGANCg0KROG6oW5nIGJp4buDdSDEkeG7kyB0aOG7qSBiYSwgaOG7r3UgZOG7pW5nIGjGoW4gY+G6oywgdsOsIG7DsyBjaG8gcGjDqXAga2jhuqNvIHPDoXQga2h1eW5oIGjGsOG7m25nIGRp4buFbiB0aeG6v24gY2h1bmcgdHJvbmcgdG/DoG4gYuG7mSAyNjEyIGNodeG7l2kgdHLhuqFuZyB0aMOhaS4gVGhlbyDEkcOzLCBraHV5bmggaMaw4bubbmcgcGjhu5UgYmnhur9uIG5o4bqldCBsw6AgbeG7mXQgc+G7sSDhu5VuIMSR4buLbmgg4bufIG3hu6ljIEcxLUcyDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpwYXIobWZyb3cgPSBjKDEsMikpDQoNCnNlcWZwbG90KHNyaF9zZXEsIA0KICAgICAgICAgICBtYWluID0gIlNlcXVlbmNlIGZyZXF1ZW5jeSIsDQogICAgICAgICAgICB3aXRoLmxlZ2VuZCA9IEYsDQogICAgICAgICBwYmFydyA9IFQpDQoNCnNlcWxlZ2VuZChzcmhfc2VxKQ0KDQpgYGANCg0KVGEgY8OybiBjw7MgdGjhu4Mgc28gc8OhbmggxJHhurdjIHTDrW5oIHBow6JuIGLhu5EgY8OhYyBz4buxIGtp4buHbi90cuG6oW5nIHRow6FpIGdp4buvYSAyIHBow6JuIG5ow7NtLCB0aMOtIGThu6UgZ2nhu5tpIHTDrW5oOg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnNlcW10cGxvdChzcmhfc2VxLCBncm91cCA9IFNSSCRzZXgpDQoNCmBgYA0KDQpWw6AgdMawxqFuZyB04buxLCBzbyBzw6FuaCBraHV5bmggaMaw4bubbmcgZGnhu4VuIHRp4bq/biBjaHVuZyBnaeG7r2EgMiBnaeG7m2kgdMOtbmgNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzZXFpcGxvdChzcmhfc2VxLCBncm91cCA9IFNSSCRzZXgsIGlkeHMgPSAwLCBib3JkZXIgPSBOQSxzcGFjZSA9IDApDQoNCmBgYA0KDQojIEtoYWkgdGjDoWMgc8OidSBoxqFuIHTDrW5oIGNo4bqldCBjaHXhu5dpIHRy4bqhbmcgdGjDoWkNCg0KVHJvbmcgYsaw4bubYyB0aeG6v3AgdGhlbywgY2jDum5nIHRhIGLhuq90IMSR4bqndSBraOG6o28gc8OhdCBzw6J1IGjGoW4gduG7gSBkaeG7hW4gdGnhur9uIGPhu6dhIGPDoWMgY2h14buXaSB0cuG6oW5nIHRow6FpIHRyb25nIGThu68gbGnhu4d1OiBN4bulYyB0acOqdSBsw6AgdMOtbmggdG/DoW4gdsOgaSB0cuG7iyBz4buRIHRo4buRbmcga8OqIHbDoCBsw6BtIGPDoWMgcGjDom4gdMOtY2ggeMOhYyBzdeG6pXQuDQoNClRy4buLIHPhu5EgdGjhu5FuZyBrw6ogxJHhuqd1IHRpw6puIGPDsyB0w6puIGzDoCBFbnRyb3B5ICh0aGVvIFNoYW5ub24pIGNobyBt4buXaSBjaHXhu5dpLiBFbnRyb3B5IMSRbyBsxrDhu51uZyB0w61uaCBjaOG6pXQgImLhuqV0IOG7lW4gxJHhu4tuaCIgY+G7p2Egdmnhu4djIHRpw6puIGzGsOG7o25nIGPDoWMgdHLhuqFuZyB0aMOhaSB0cm9uZyBjaHXhu5dpLiBO4bq/dSBt4buZdCBjaHXhu5dpIGNo4buJIGNo4bupYSB0b8OgbiAxIHRy4bqhbmcgdGjDoWksIGVudHJvcHk9MDsgZW50cm9weSBj4bunYSBt4buZdCBjaHXhu5dpIGPDsyDEkeG7mSBkw6BpIDEyIHRo4budaSDEkWnhu4NtIHbDoCBjw7MgNCB0cuG6oW5nIHRow6FpIHPhur0gxJHhuqF0IGPhu7FjIMSR4bqhaSA9IDEuMzg2IG7hur91IG3hu5dpIHRy4bqhbmcgdGjDoWkgeOG6o3kgcmEgxJHDum5nIDMgbOG6p24uIA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnNyaF9zZXElPiVzZXFpZW50KCklPiVoZWFkKCkNCg0Kc3JoX3NlcSU+JXNlcWllbnQoKSU+JWFwcGx5KC4sMixtZWFuKQ0KYGBgDQoNCkvhur90IHF14bqjIHRyw6puIHRyw6xuaCBiw6B5IGdpw6EgdHLhu4sgRW50cm9weSBj4bunYSA2IG5nxrDhu51pIMSR4bqndSB0acOqbiwgdsOgIGdpw6EgdHLhu4sgRW50cm9weSB0cnVuZyBiw6xuaCBjaG8gdG/DoG4gYuG7mSBt4bqrdSBsw6AgMC4zNzE0DQoNCkdoaSBjaMO6IGzDoCBlbnRyb3B5IGtow7RuZyB4w6l0IMSR4bq/biB0aOG7qSB04buxIHRoYXkgxJHhu5VpIGPhu6dhIGPDoWMgc+G7sSBraeG7h24vdHLhuqFuZyB0aMOhaSwgbmjGsG5nIGNo4buJIMSR4bq/bSBz4buRIGzhuqduIHRy4bqhbmcgdGjDoWkgxJHDsyB44bqjeSByYS4NCg0KTeG7mXQgdHLhu4sgc+G7kSBraMOhYyBsw6AgVHVyYnVsZW5jZSAodGhlbyBFbHppbmdhLCAyMDA2KSBjxaluZyBtw7QgdOG6oyB0w61uaCBi4bqldCDEkeG7i25oIGPhu6dhIGNodeG7l2ksIG5oxrBuZyBjaOG7i3UgY2hpIHBo4buRaSBj4bunYSB0csOsbmggdOG7sSBjw6FjIHRy4bqhbmcgdGjDoWk6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kc3JoX3NlcSU+JXNlcWllbnQoKSU+JWhpc3QoY29sPSJnb2xkIix4bGFiID0gIkVudHJvcHkiKQ0KDQpzcmhfc2VxJT4lc2VxU1QoKSU+JWhlYWQoKQ0KDQpzcmhfc2VxJT4lc2VxU1QoKSU+JWhpc3QoY29sPSJyZWQiLHhsYWIgPSAiVHVyYnVsZW5jZSIpDQpgYGANCg0KVGEgY8OzIHRo4buDIGto4bqjbyBzw6F0IHTGsMahbmcgcXVhbiBnaeG7r2EgRW50cm9weSB2w6AgVHVyYnVsZW5jZToNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpTUkglPiVtdXRhdGUoRW50cm9weT1zZXFpZW50KHNyaF9zZXEpLA0KICAgICAgICAgICAgICAgICBUdXJidWxlbmNlPXNlcVNUKHNyaF9zZXEpKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VHVyYnVsZW5jZSx5PUVudHJvcHkpKSsNCiAgZ2VvbV9wb2ludChhbHBoYT0wLjIpKw0KICBnZW9tX3Ntb290aChjb2w9InJlZCIsc2U9RikrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpWw6Agc28gc8OhbmggRW50cm9weSBnaeG7r2EgMTAgbmjDs20gdHXhu5VpIChuxINtIHNpbmgpDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KU1JIJT4lbXV0YXRlKEVudHJvcHk9c2VxaWVudChzcmhfc2VxKSwNCiAgICAgICAgICAgICAgICAgQllHcm91cD1jdXQoU1JIJGJpcnRoeSxiPTEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSklPiUNCiAgZ2dwbG90KGFlcyh4PUJZR3JvdXAseT1FbnRyb3B5KSkrDQogIGx2cGxvdDo6Z2VvbV9sdihjb2w9ImJsYWNrIiwNCiAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRiwNCiAgICAgICAgICAgICAgICAgIGFlcyhmaWxsPS4uTFYuLiksDQogICAgICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlID0gMykrDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNwZWN0cmFsIikrDQogIHRoZW1lX2J3KCkrY29vcmRfZmxpcCgpDQpgYGANCg0KQmnhu4N1IMSR4buTIG7DoHkgY2hvIHRo4bqleSBOaOG7r25nIG5nxrDhu51pIGdpw6AgdsOgIHRydW5nIG5pw6puIGPDsyBz4buxIGLhuqV0IMSR4buLbmggY2FvIHRyb25nIGRp4buFbiB0aeG6v24gc+G7qWMga2jhu49lLCBiaeG7g3UgaGnhu4duIGLhurFuZyBnacOhIHRy4buLIGVudHJvcHkgY2FvLCBzbyB24bubaSBuaOG7r25nIG5nxrDhu51pIHRy4bq7IHR14buVaSAoc2luaCBzYXUgMTk3OCkuDQoNClRp4bq/cCB0aGVvLCBjaMO6bmcgdGEgc+G6vSBsw6BtIG3hu5l0IHBow6JuIHTDrWNoIHjDoWMgc3XhuqV0IMSRxqFuIGdp4bqjbiwgbmjhurFtIGto4bqjbyBzw6F0IGto4bqjIG7Eg25nIGJp4bq/biDEkeG7lWkgdOG7qyBt4buZdCB0cuG6oW5nIHRow6FpIHPhu6ljIGto4buPZSB4w6FjIMSR4buLbmggKHRow60gZOG7pSBHMSkg4bufIHRo4budaSDEkWnhu4NtIHQgc2FuZyBt4buZdCB0cuG6oW5nIHRow6FpIGtow6FjIOG7nyB0aOG7nWkgxJFp4buDbSB0KzEgKGJhbyBn4buTbSBz4buxIGR1eSB0csOsIGNow61uaCBuw7MpOg0KDQpQaMOibiB0w61jaCBuw6B5IGfhu5NtIDIgYsaw4bubYyA6DQoNCsSQ4bqndSB0acOqbiB0YSBz4bq9IHThuqFvIG1hdHJpeCB4w6FjIHN14bqldCBjaG8gdOG7q25nIGPhurdwIHRy4bqhbmcgdGjDoWk6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcG1hdD1zZXF0cmF0ZShzcmhfc2VxKSAlPiUgYXMubWF0cml4KCkNCg0KcG1hdA0KDQpjb2xuYW1lcyhwbWF0KT1jKCJCMSIsIkIyIiwiTSIsIkcxIiwiRzIiKQ0Kcm93bmFtZXMocG1hdCk9YygiQjEiLCJCMiIsIk0iLCJHMSIsIkcyIikNCg0KYGBgDQoNCkvhur90IHF14bqjIG7DoHkgcuG6pXQgdGjDuiB24buLLCB2w6AgaOG7o3AgbG9naWMsIHRhIGPDsyB0aOG7gyBkaeG7hW4gZ2nhuqNpIG7DsyBt4buZdCBjw6FjaCBwaG9uZyBwaMO6LCB0aMOtIGThu6U6IE3hu5l0IG5nxrDhu51pIGto4buPZSBt4bqhbmggdsOgIGzhuqFjIHF1YW4gKEcxKSBjw7Mgbmhp4buBdSBjxqEgbWF5IHRp4bq/cCB04bulYyBraOG7j2UgbeG6oW5oIHbDoG8gbsSDbSBzYXUsIG3hu5l0IG5nxrDhu51pIGLhu4duaCB04bqtdCAoQjEpIHbhuqtuIGPDsyDEkeG6v24gNDIlIMSR4bqhdCBt4bupYyB0cnVuZyBiw6xuaCAoTSkgdsOgbyBuxINtIHRp4bq/cCB0aGVvLCBuaMawbmcgcuG6pXQgw610IGPGoSBtYXkgdHLhu58gduG7gSBt4bupYyBraOG7j2UgbeG6oW5oICgzLjUlKS4gLi4uDQoNCkvhur90IHF14bqjIG7DoHkgY8OzIHRo4buDIMSRxrDhu6NjIGNodXnhu4NuIHRow6BuaCBt4buZdCBiaeG7g3UgxJHhu5MgbeG6oW5nIChuZXR3b3JrKQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2dyYXBoKQ0KDQpjZz1kYXRhLmZyYW1lKHJvdz1yb3duYW1lcyhwbWF0KVtyb3cocG1hdCldLCANCiAgICAgICAgICAgICAgIGNvbD1jb2xuYW1lcyhwbWF0KVtjb2wocG1hdCldLCANCiAgICAgICAgICAgICAgIFByb2I9cG1hdCU+JWFzLnZlY3RvcigpKQ0KDQpuYW1lcyhjZyk9YygiZnJvbSIsInRvIiwiUHJvYiIpDQoNCmxpYnJhcnkoaWdyYXBoKQ0KDQpncmFwaDwtZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGNnKQ0KDQpnZ3JhcGgoZ3JhcGgsY2lyY3VsYXI9RixsYXlvdXQgPSAnbGdsJykrDQogIGdlb21fZWRnZV9mYW4oYWVzKGNvbD1Qcm9iLGxhYmVsPXJvdW5kKFByb2IsMikpLA0KICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gVCwNCiAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoNSwgJ21tJykpLA0KICAgICAgICAgICAgICAgIGFscGhhPTAuNSkrDQogIGdlb21fZWRnZV9sb29wKGFlcyhjb2w9UHJvYixsYWJlbD1yb3VuZChQcm9iLDIpKSxzcGFuPTEsYWxwaGE9MC41KSsNCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IG5hbWUpKSsNCiAgY29vcmRfZml4ZWQoKSsNCiAgc2NhbGVfZWRnZV9jb2xvcl9ncmFkaWVudChsb3c9ImJsdWUiLGhpZ2g9InJlZCIpKw0KICB0aGVtZV9ncmFwaCgpDQpgYGANCg0KVGnhur9wIHRoZW8sIHRhIGPDsyB0aOG7gyDDoXAgZOG7pW5nIHBoxrDGoW5nIHBow6FwIENsdXN0ZXJpbmcgxJHhu4MgcGjDom4gY+G7pW0gMjAwIGPDoSB0aOG7gyBjaOG7jW4gbmfhuqt1IG5oacOqbiB0cm9uZyBt4bqrdSBk4buxYSB2w6BvIMSR4bq3YyB0w61uaCBj4bunYSBjaHXhu5dpIHRy4bqhbmcgdGjDoWkuDQoNCsSQ4bqndSB0acOqbiwgdGEgY2jhu41uIG5n4bqrdSBuaGnDqm4gMjAwIGPDoSB0aOG7gywgcuG7k2kgZOG7sW5nIG1hdHJpeCBnacOhIHRy4buLIGRpc3RhbmNlIHRoZW8gcGjGsMahbmcgcGjDoXAgTENTIChMb25nZXN0IENvbW1vbiBTdWJzZXF1ZW5jZSkuIFNhdSDEkcOzIHRhIGTDuW5nIHBoxrDGoW5nIHBow6FwIHBow6JuIGPhu6VtIGPhu6dhIFdhcmQuDQoNCkvhur90IHF14bqjIHBow6JuIGPhu6VtIGPDsyB0aOG7gyDEkcaw4bujYyB0csOsbmggYsOgeSBi4bqxbmcgZGVuZG9ncmFtIGTGsOG7m2kgMyBow6xuaCB0aOG7qWM6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpzZXQuc2VlZCgxMjMpDQoNCmxpYnJhcnkoY2x1c3RlcikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCg0Kc3JoX3NlcTIwMDwtc2VxZGVmKHNhbXBsZV9uKFNSSCwyMDApLDU6MTUsDQogICAgICAgICAgICAgICAgICBzdGF0ZXM9YygiQjEiLCJCMiIsIk0iLCJHMSIsIkcyIiksDQogICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm90VyIsIkJhZCIsIkF2ZXJhZ2UiLCJWZXJ5VyIsIldlbGwiKSwNCiAgICAgICAgICAgICAgICAgIGNwYWw9cGFsczo6YnJld2VyLnJkeWxnbig1KSkNCg0KY29sbmFtZXMoc3JoX3NlcTIwMCk9YygxOTk5OjIwMDkpDQoNCmRtYXQ9c2VxZGlzdChzcmhfc2VxMjAwLCBtZXRob2QgPSAiTENTIix3aXRoLm1pc3NpbmcgPSBULG5vcm09VFJVRSkNCg0KY2x1c3RlcndhcmQgPC0gYWduZXMoZG1hdCwgZGlzcyA9IFRSVUUsIG1ldGhvZCA9ICJ3YXJkIikNCmBgYA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmZ2aXpfZGVuZChjbHVzdGVyd2FyZCAsIGsgPSA2LCAjIEN1dCBpbiA2IGdyb3Vwcw0KICAgICAgICAgIGNleCA9IDAuNSwgIyBsYWJlbCBzaXplDQogICAgICAgICAga19jb2xvcnMgPSBjKCIjMjRhM2YyIiwiIzJlZGI5YiIsIiMyNDNmZjIiLCIjZjJhNjI0IiwiI2E3NTlmOSIsICIjZjk1OTg3IiksDQogICAgICAgICAgY29sb3JfbGFiZWxzX2J5X2sgPSBUUlVFLCAjIGNvbG9yIGxhYmVscyBieSBncm91cHMNCiAgICAgICAgICByZWN0ID0gVFJVRSwgIyBBZGQgcmVjdGFuZ2xlIGFyb3VuZCBncm91cHMNCiAgICAgICAgICByZWN0X2JvcmRlciA9IGMoIiMyNGEzZjIiLCIjMmVkYjliIiwiIzI0M2ZmMiIsIiNmMmE2MjQiLCAiI2E3NTlmOSIsICIjZjk1OTg3IiksDQogICAgICAgICAgcmVjdF9maWxsID0gVFJVRSkNCiAgICAgICAgICANCg0KZnZpel9kZW5kKGNsdXN0ZXJ3YXJkICwgayA9IDYsICMgQ3V0IGluIDYgZ3JvdXBzDQogICAgICAgICAgY2V4ID0gMC41LCAjIGxhYmVsIHNpemUNCiAgICAgICAgICBrX2NvbG9ycyA9IGMoIiMyNGEzZjIiLCIjMmVkYjliIiwiIzI0M2ZmMiIsIiNmMmE2MjQiLCIjYTc1OWY5IiwgIiNmOTU5ODciKSwNCiAgICAgICAgICBjb2xvcl9sYWJlbHNfYnlfayA9IFRSVUUsICMgY29sb3IgbGFiZWxzIGJ5IGdyb3Vwcw0KICAgICAgICAgIHR5cGUgPSAiY2lyY3VsYXIiKQ0KDQpsaWJyYXJ5KCJpZ3JhcGgiKQ0KDQpmdml6X2RlbmQoY2x1c3RlcndhcmQsIGsgPSA2LCAjIEN1dCBpbiA2IGdyb3Vwcw0KICAgICAgICAgIGtfY29sb3JzID0gYygiIzI0YTNmMiIsIiMyZWRiOWIiLCIjMjQzZmYyIiwiI2YyYTYyNCIsIiNhNzU5ZjkiLCAiI2Y5NTk4NyIpLCAgICAgICAgIA0KICAgICAgICAgIHR5cGUgPSAicGh5bG9nZW5pYyIsIHJlcGVsID0gVFJVRSwNCiAgICAgICAgICBwaHlsb19sYXlvdXQgPSAibGF5b3V0LmdlbSIpDQpgYGANCg0KTMawdSDDvTog4bueIMSRw6J5IHRhIGdp4bqjIMSR4buLbmggcGjDom4gZOG7ryBsaeG7h3UgdGjDoG5oIDYgY+G7pW0uDQoNClRhIGPDsyB0aOG7gyBnaeG6o20gc+G7kSBsxrDhu6NuZyBjw7JuIDMgY2x1c3Rlciwgbmjhu69uZyBiaeG7g3UgxJHhu5Mgc2F1IMSRw6J5IGNobyBwaMOpcCBzbyBzw6FuaCDEkeG6t2MgdMOtbmggdHLDrG5oIHThu7EgY2h14buXaSB0cuG6oW5nIHRow6FpIGdp4buvYSAzIGNsdXN0ZXI6DQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KY2x1c3RlcjMgPC0gY3V0cmVlKGNsdXN0ZXJ3YXJkLCBrID0gMykNCmNsdXN0ZXIzIDwtIGZhY3RvcihjbHVzdGVyMywgbGFiZWxzID0gYygiQ2x1c3RlciAxIiwgIkNsdXN0ZXIgMiIsICJDbHVzdGVyIDMiKSkNCg0Kc2VxZnBsb3Qoc3JoX3NlcTIwMCwgZ3JvdXAgPSBjbHVzdGVyMywgcGJhcncgPSBUKQ0KDQpzZXFtdHBsb3Qoc3JoX3NlcTIwMCwgZ3JvdXAgPSBjbHVzdGVyMykNCmBgYA0KDQpL4bq/dCBxdeG6oyBuw6B5IGNobyB0aOG6pXkgQ2x1c3RlciAxIGfhu5NtIG5o4buvbmcgbmfGsOG7nWkga2jhu49lIG3huqFuaCBuaOG6pXQgKEcxKSwgZGnhu4VuIHRp4bq/biBz4bupYyBraOG7j2UgY+G7p2EgaOG7jSB0cm9uZyAxMCBuxINtIGRhbyDEkeG7mW5nIGdp4buvYSBHMSB2w6AgRzIgduG7m2kgw610IG5ndXkgY8ahIGdp4bqjbSB4deG7kW5nIG3hu6ljIEIxIGhheSBCMjsgQ2x1c3RlciAyIGfhu5NtIG5o4buvbmcgbmfGsOG7nWkgw610IGzhuqFjIHF1YW4gaMahbiBuaMawbmcgY8OzIHTDrG5oIHRy4bqhbmcgc+G7qWMga2jhu49lIHLhuqV0IHThu5F0IHbDoCDhu5VuIMSR4buLbmggduG7m2kgc+G7ryBkdXkgdHLDrCBt4bupYyBHMjsgQ3Xhu5FpIGPDuW5nLCBjbHVzdGVyIDMgZ+G7k20gbmjhu69uZyBuZ8aw4budaSBjw7MgdHLhuqFuZyB0aMOhaSBz4bupYyBraOG7j2Uga2jDtG5nIOG7lW4gxJHhu4tuaCB2w6AgZGFvIMSR4buZbmcgcXVhbmggbeG7qWMgdHJ1bmcgYsOsbmggL3jhuqV1LCB24bubaSBuZ3V5IGPGoSBjYW8geOG6o3kgcmEgdHLhuqFuZyB0aMOhaSBCMSwgdGjhuq1tIGNow60gQjINCg0KIyBL4bq/dCBsdeG6rW4NCg0KQsOgaSB0aOG7sWMgaMOgbmggbmfhuq9uIGjDtG0gbmF5IMSRw6MgZ2nhu5tpIHRoaeG7h3UgduG7m2kgY8OhYyBi4bqhbiBt4buZdCDDvSB0xrDhu59uZyBt4bubaSBjw7MgdGjhu4Mgw6FwIGThu6VuZyBjaG8gbmdoacOqbiBj4bupdSBsw6JtIHPDoG5nL2Thu4tjaCB04buFIHbhu5tpIHRoaeG6v3Qga+G6vyB0csaw4budbmcgZGnhu4VuL3RoZW8gZMO1aSBrw6lvIGTDoGkgdsOgIGvhur90IHF14bqjIGzDoCBt4buZdCBiaeG6v24gcuG7nWkgcuG6oWMgbmhp4buBdSBnacOhIHRy4buLLiDDnSB0xrDhu59uZyBuw6B5IMSR4buZYyDEkcOhbyBoxqFuIHNvIHbhu5tpIG5naGnDqm4gY+G7qXUgdGhlbyBkw7VpIGJp4bq/biDEkeG7i25oIGzGsOG7o25nIHRoZW8gdGjhu51pIGdpYW4gaG/hurdjIHN1cnZpdmFsIGFuYWx5c2lzLHbhu5tpIG5oaeG7gXUg4bupbmcgZOG7pW5nIHRp4buBbSBuxINuZy4gTmhpIGPFqW5nIGdp4bubaSB0aGnhu4d1IHPGoSBsxrDhu6NjIHbhu4EgbeG7mXQgY8O0bmcgY+G7pSB0cm9uZyBSIGNobyBwaMOpcCBraOG6o28gc8OhdCBjaHXhu5dpIHPhu7Ega2nhu4duL3Ry4bqhbmcgdGjDoWkuIEzGsHUgw70gcuG6sW5nIHRyb25nIGLDoGkgY2jDum5nIHRhIGNo4buJIG3hu5tpIGto4bqjbyBzw6F0IGtob+G6o25nIDMwJSBuaOG7r25nIHTDrW5oIG7Eg25nIG3DoCBwYWNrYWdlIFRyYU1pbmVSIGN1bmcgY+G6pXAuIE5nb8OgaSByYSwgbeG7mXQgcGFja2FnZSBraMOhYyBjw7MgdMOqbiBsw6AgUFNUIGPDsm4gY2hvIHBow6lwIGThu7FuZyBtw7QgaMOsbmggY8OieSB0acOqbiDEkW/DoW4gY2h14buXaSBz4buxIGtp4buHbiBtw6AgTmhpIHPhur0gZ2nhu5tpIHRoaeG7h3UgdHJvbmcgbeG7mXQgZOG7i3Aga2jDoWMuDQoNCkNow7pjIGPDoWMgYuG6oW4gdGjhu7FjIGjDoG5oIHZ1aSwgdsOgIMOhcCBk4bulbmcgxJHGsOG7o2Mgw70gdMaw4bufbmcgbeG7m2kgbsOgeSB2w6BvIG5naGnDqm4gY+G7qXUgY+G7p2EgcmnDqm5nIG3DrG5oLg==