Phần 1: Tìm hiểu và Chuẩn bị dữ liệu

# Đọc bộ dữ liệu
d <- read.csv("D:/naaaaaa/PTDLDT/Supermarket Transactions.csv", header = T)

1. Cấu trúc bộ dữ liệu

# Cấu trúc bộ dữ liệu
str(d)
## 'data.frame':    14059 obs. of  16 variables:
##  $ X                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ PurchaseDate     : chr  "2007-12-18" "2007-12-20" "2007-12-21" "2007-12-21" ...
##  $ CustomerID       : int  7223 7841 8374 9619 1900 6696 9673 354 1293 7938 ...
##  $ Gender           : chr  "F" "M" "F" "M" ...
##  $ MaritalStatus    : chr  "S" "M" "M" "M" ...
##  $ Homeowner        : chr  "Y" "Y" "N" "Y" ...
##  $ Children         : int  2 5 2 3 3 3 2 2 3 1 ...
##  $ AnnualIncome     : chr  "$30K - $50K" "$70K - $90K" "$50K - $70K" "$30K - $50K" ...
##  $ City             : chr  "Los Angeles" "Los Angeles" "Bremerton" "Portland" ...
##  $ StateorProvince  : chr  "CA" "CA" "WA" "OR" ...
##  $ Country          : chr  "USA" "USA" "USA" "USA" ...
##  $ ProductFamily    : chr  "Food" "Food" "Food" "Food" ...
##  $ ProductDepartment: chr  "Snack Foods" "Produce" "Snack Foods" "Snacks" ...
##  $ ProductCategory  : chr  "Snack Foods" "Vegetables" "Snack Foods" "Candy" ...
##  $ UnitsSold        : int  5 5 3 4 4 3 4 6 1 2 ...
##  $ Revenue          : num  27.38 14.9 5.52 4.44 14 ...

Tập dữ liệu “Supermarket Transactions” gồm tổng cộng 14.059 dòng dữ liệu, tương ứng với 14.059 giao dịch mua hàng được ghi nhận tại siêu thị. Mỗi dòng dữ liệu đại diện cho một giao dịch riêng lẻ và bao gồm thông tin chi tiết về khách hàng, sản phẩm, cũng như các đặc điểm liên quan đến hành vi tiêu dùng. Bộ dữ liệu này có tổng cộng 16 biến (cột), bao gồm cả biến định tính và định lượng, phản ánh đầy đủ các yếu tố nhân khẩu học, địa lý và kết quả kinh doanh từ mỗi giao dịch.

# Hiện thị các dòng đầu của bộ dữ liệu
head(d)
##   X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children
## 1 1   2007-12-18       7223      F             S         Y        2
## 2 2   2007-12-20       7841      M             M         Y        5
## 3 3   2007-12-21       8374      F             M         N        2
## 4 4   2007-12-21       9619      M             M         Y        3
## 5 5   2007-12-22       1900      F             S         Y        3
## 6 6   2007-12-22       6696      F             M         Y        3
##    AnnualIncome          City StateorProvince Country ProductFamily
## 1   $30K - $50K   Los Angeles              CA     USA          Food
## 2   $70K - $90K   Los Angeles              CA     USA          Food
## 3   $50K - $70K     Bremerton              WA     USA          Food
## 4   $30K - $50K      Portland              OR     USA          Food
## 5 $130K - $150K Beverly Hills              CA     USA         Drink
## 6   $10K - $30K Beverly Hills              CA     USA          Food
##   ProductDepartment      ProductCategory UnitsSold Revenue
## 1       Snack Foods          Snack Foods         5   27.38
## 2           Produce           Vegetables         5   14.90
## 3       Snack Foods          Snack Foods         3    5.52
## 4            Snacks                Candy         4    4.44
## 5         Beverages Carbonated Beverages         4   14.00
## 6              Deli          Side Dishes         3    4.37
# Hiển thị các dòng cuối của bộ dữ liệu
tail(d)
##           X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children
## 14054 14054   2009-12-29       2032      F             M         N        3
## 14055 14055   2009-12-29       9102      F             M         Y        2
## 14056 14056   2009-12-29       4822      F             M         Y        3
## 14057 14057   2009-12-31        250      M             S         Y        1
## 14058 14058   2009-12-31       6153      F             S         N        4
## 14059 14059   2009-12-31       3656      M             S         N        3
##       AnnualIncome        City StateorProvince Country  ProductFamily
## 14054  $10K - $30K      Yakima              WA     USA Non-Consumable
## 14055  $10K - $30K   Bremerton              WA     USA           Food
## 14056  $10K - $30K Walla Walla              WA     USA           Food
## 14057  $30K - $50K    Portland              OR     USA          Drink
## 14058  $50K - $70K     Spokane              WA     USA          Drink
## 14059  $50K - $70K    Portland              OR     USA Non-Consumable
##       ProductDepartment      ProductCategory UnitsSold Revenue
## 14054         Household       Paper Products         5   14.50
## 14055      Baking Goods         Baking Goods         3    9.64
## 14056      Frozen Foods           Vegetables         3    7.45
## 14057         Beverages Pure Juice Beverages         4    3.24
## 14058             Dairy                Dairy         2    4.00
## 14059         Household           Electrical         5   25.53

2. Kiểm tra dữ liệu thiếu

# Kiểm tra dữ liệu thiếu
sum(is.na(d))
## [1] 0

Bộ dữ liệu không có dữ liệu thiếu

3. Chuyển dạng dữ liệu

d$Gender <- as.factor(d$Gender)
d$MaritalStatus <- as.factor(d$MaritalStatus)
d$Homeowner <- as.factor(d$Homeowner)
d$AnnualIncome <- as.factor(d$AnnualIncome)
# Tạo biến định tính
bdt <- c( "PurchaseDate",
"CustomerID", "Gender", "MaritalStatus", "Homeowner", "AnnualIncome", "City", "StateorProvince","Country", "ProductFamily", "ProductDepartment", "ProductCategory" )

# Tạo bộ dữ liệu mới chỉ có biến định tính
dt <- d[, bdt]

# Xuất bộ dữ liệu định tính
head(dt)
##   PurchaseDate CustomerID Gender MaritalStatus Homeowner  AnnualIncome
## 1   2007-12-18       7223      F             S         Y   $30K - $50K
## 2   2007-12-20       7841      M             M         Y   $70K - $90K
## 3   2007-12-21       8374      F             M         N   $50K - $70K
## 4   2007-12-21       9619      M             M         Y   $30K - $50K
## 5   2007-12-22       1900      F             S         Y $130K - $150K
## 6   2007-12-22       6696      F             M         Y   $10K - $30K
##            City StateorProvince Country ProductFamily ProductDepartment
## 1   Los Angeles              CA     USA          Food       Snack Foods
## 2   Los Angeles              CA     USA          Food           Produce
## 3     Bremerton              WA     USA          Food       Snack Foods
## 4      Portland              OR     USA          Food            Snacks
## 5 Beverly Hills              CA     USA         Drink         Beverages
## 6 Beverly Hills              CA     USA          Food              Deli
##        ProductCategory
## 1          Snack Foods
## 2           Vegetables
## 3          Snack Foods
## 4                Candy
## 5 Carbonated Beverages
## 6          Side Dishes

Phần 2: Phân tích mô tả một biến Định tính

1. Gender

# Lập bảng tần số
gioitinh <- table(dt$Gender)
gioitinh
## 
##    F    M 
## 7170 6889
# Lập bảng tần suất
table(dt$Gender)/sum(nrow(dt))
## 
##         F         M 
## 0.5099936 0.4900064
library(ggplot2)

# Đưa về dạng data frame
df_gioitinh <- as.data.frame(gioitinh)
colnames(df_gioitinh) <- c("Gender", "Count")

# Tính phần trăm và thêm vào data frame
df_gioitinh$Percent <- round(df_gioitinh$Count / sum(df_gioitinh$Count) * 100, 1)

# Tạo nhãn ghép với %
df_gioitinh$Label <- paste0(df_gioitinh$Percent, "%")

# Vẽ biểu đồ tròn
ggplot(df_gioitinh, aes(x = "", y = Count, fill = Gender)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
  labs(title = "Biểu đồ 1: Biểu đồ phân bố giới tính") +  # 👈 Tiêu đề
  theme_void() + # bỏ đi các viền, mấy cái chú thích x, y đồ á
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))  # 👈 Cho hiện và chỉnh tiêu đề

Vậy trong bộ dữ liệu này có 7170 khách hàng là nữ chiếm tỉ lệ 51% và 6889 khách hàng là nam chiếm tỉ lệ 49% .

2. MarialStatus

# Lập bảng tần số
honnhan <- table(dt$MaritalStatus)
honnhan
## 
##    M    S 
## 6866 7193
# Lập bảng tần suất
table(dt$MaritalStatus)/sum(nrow(dt))
## 
##         M         S 
## 0.4883704 0.5116296
library(ggplot2)

df_honnhan <- as.data.frame(honnhan)
colnames(df_honnhan) <- c("MaritalStatus", "Count")

df_honnhan$Percent <- round(df_honnhan$Count / sum(df_honnhan$Count) * 100, 1)
df_honnhan$Label <- paste0(df_honnhan$Percent, "%")

ggplot(df_honnhan, aes(x = "", y = Count, fill = MaritalStatus)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
  labs(title = "Biểu đồ 2: Biểu đồ phân bố tình trạng hôn nhân") +
  theme_void() +
   scale_fill_manual(
    values = c("M" = "red", "S" = "green"),
    labels = c("M" = "Đã kết hôn", "S" = "Độc thân")
  ) +
  theme (plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

Vậy trong bộ dữ liệu này có 6866 khách hàng đã kết hôn chiếm tỉ lệ 48.8% và 7193 khách hàng độc thân chiếm tỉ lệ 51.2%.

3. Homeowner

# Lập bảng tần số
nha <- table(dt$Homeowner)
nha
## 
##    N    Y 
## 5615 8444
# Lập bảng tần suất
table(dt$Homeowner)/sum(nrow(dt))
## 
##         N         Y 
## 0.3993883 0.6006117
# Đưa về dạng data.frame
df_nha <- as.data.frame(nha)
colnames(df_nha) <- c("Homeowner", "Count")

df_nha$Percent <- round(df_nha$Count / sum(df_nha$Count) * 100, 1)
df_nha$Label <- paste0(df_nha$Percent, "%")

ggplot(df_nha, aes(x = "", y = Count, fill = Homeowner)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
  labs(title = "Biểu đồ 3: Biểu đồ thể hiện tình trạng nhà ở") +
  theme_void() +
  scale_fill_manual(
    values = c("Y" = "steelblue", "N" = "tomato"),
    labels = c("Y" = "Đã có nhà", "N" = "Chưa có nhà")
  ) +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

Vậy trong bộ dữ liệu này có 5615 khách hàng chưa có nhà chiếm tỉ lệ 39.9% và 8444 khách hàng đã có nhà chiếm tỉ lệ 60.1%.

4. AnnualIncome

# Lập bảng tần số
thunhap <- table(dt$AnnualIncome)
thunhap
## 
##   $10K - $30K $110K - $130K $130K - $150K       $150K +   $30K - $50K 
##          3090           643           760           273          4601 
##   $50K - $70K   $70K - $90K  $90K - $110K 
##          2370          1709           613
# Lập bảng tần suất
table(dt$AnnualIncome)/sum(nrow(dt))
## 
##   $10K - $30K $110K - $130K $130K - $150K       $150K +   $30K - $50K 
##    0.21978804    0.04573583    0.05405790    0.01941817    0.32726367 
##   $50K - $70K   $70K - $90K  $90K - $110K 
##    0.16857529    0.12155914    0.04360196
library(ggplot2)

# Chuyển sang dạng data frame
df_thunhap <- as.data.frame(thunhap)
colnames(df_thunhap) <- c("AnnualIncome", "Count")

df_thunhap$Percent <- round(df_thunhap$Count / sum(df_thunhap$Count) * 100, 1)
df_thunhap$Label <- paste0(df_thunhap$Percent, "%")

# Vẽ biểu đồ cột
ggplot(df_thunhap, aes(x = AnnualIncome, y = Count, fill = AnnualIncome)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = Count), vjust = -0.5) +
  labs(title = "Biểu đồ 4: Biểu đồ phân bố mức thu nhập",
       x = "Khoảng thu nhập theo nhóm",
       y = "Số người") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
        legend.position = "none")  # Ẩn chú thích màu vì đã hiện trên trục x

Vậy trong bộ dữ liệu này có:

  • 3090 khách hàng thuộc nhóm thu nhập $10K - $30K, chiếm tỷ lệ 22%.
  • 643 khách hàng thuộc nhóm thu nhập $110K - $130K, chiếm tỷ lệ 4.6%.
  • 760 khách hàng thuộc nhóm thu nhập $120K - $150K, chiếm tỷ lệ 5.4%.
  • 273 khách hàng thuộc nhóm thu nhập $150K + , chiếm tỷ lệ 1.9%.
  • 4601 khách hàng thuộc nhóm thu nhập $30K - $50K, chiếm tỷ lệ 32.7%.
  • 2370 khách hàng thuộc nhóm thu nhập $50K - $70K, chiếm tỷ lệ 16.9%.
  • 1709 khách hàng thuộc nhóm thu nhập $70K - $90K, chiếm tỷ lệ 12.2%.
  • 613 khách hàng thuộc nhóm thu nhập $90K - $110K, chiếm tỷ lệ 4.4%.

5. City

# Lập bảng tần số
thanhpho <- table(dt$City)
thanhpho
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##           383           143           811           834           452 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##            75           845           926           654           194 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##           464           876          1386           621           866 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##           130           922           875          1257           633 
##      Victoria   Walla Walla        Yakima 
##           176           160           376
# Lập bảng tần suất
table(dt$City)/sum(nrow(dt))
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##   0.027242336   0.010171420   0.057685468   0.059321431   0.032150224 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##   0.005334661   0.060103848   0.065865282   0.046518245   0.013798990 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##   0.033003770   0.062308841   0.098584537   0.044170994   0.061597553 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##   0.009246746   0.065580767   0.062237712   0.089408920   0.045024539 
##      Victoria   Walla Walla        Yakima 
##   0.012518671   0.011380610   0.026744434
library(ggplot2)

# Chuyển sang dạng data frame
df_thanhpho <- as.data.frame(thanhpho)
colnames(df_thanhpho) <- c("City", "Count")

df_thanhpho$Percent <- round(df_thanhpho$Count / sum(df_thanhpho$Count) * 100, 1)
df_thanhpho$Label <- paste0(df_thanhpho$Percent, "%")

# Vẽ biểu đồ cột
ggplot(df_thanhpho, aes(x = City, y = Count, fill = City)) +
  geom_col(width = 0.8) +
  geom_text(aes(label = Count), vjust = -0.5) +
  labs(title = "Biểu đồ 5: Biểu đồ phân bố theo thành phố",
       x = "Thành phố", 
       y = "Số người",
       fill = "City") + 
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    axis.text.x = element_blank(), # Ẩn chữ dưới trục X
    legend.position = "bottom")

Trong bộ dữ liệu này có tổng cộng 23 thành phố. Trong đó:

  • Thành phố có số lượng khách hàng nhiều nhất là Salem với 1386 người chiếm tỉ lệ 9.9%,

  • Tiếp theo là Tacoma với 8.9% , Los Angeles và Seattle với tỉ lệ lần lượt là 6.6% và 6.6%.

Các thành phố còn lại chiếm tỷ lệ nhỏ hơn, dao động từ r df_thanhpho\(Label[4] đến r df_thanhpho\)Label[10].

6. StateorProvince

# Lập bảng tần số
tinh <- table(dt$StateorProvince)
tinh
## 
##        BC        CA        DF  Guerrero   Jalisco        OR  Veracruz        WA 
##       809      2733       815       383        75      2262       464      4567 
##   Yucatan Zacatecas 
##       654      1297
# Lập bảng tần suất
table(dt$StateorProvince)/sum(nrow(dt))
## 
##          BC          CA          DF    Guerrero     Jalisco          OR 
## 0.057543211 0.194395049 0.057969984 0.027242336 0.005334661 0.160893378 
##    Veracruz          WA     Yucatan   Zacatecas 
## 0.033003770 0.324845295 0.046518245 0.092254072
library(ggplot2)

# Chuyển sang dạng data frame
df_tinh <- as.data.frame(tinh)
colnames(df_tinh) <- c("StateorProvince", "Count")

df_tinh$Percent <- round(df_tinh$Count / sum(df_tinh$Count) * 100, 1)
df_tinh$Label <- paste0(df_tinh$Percent, "%")

# Vẽ biểu đồ cột
ggplot(df_tinh, aes(x = StateorProvince, y = Count, fill = StateorProvince)) +
  geom_col(width = 0.8) +
  geom_text(aes(label = Count), vjust = -0.5) +
  labs(title = "Biểu đồ 6: Biểu đồ phân bố theo tỉnh",
       x = "Tỉnh", 
       y = "Số người",
       fill = "StateorProvince") + 
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    axis.text.x = element_blank()) # Ẩn chữ dưới trục X

Trong bộ dữ liệu này có tổng cộng 10 tỉnh. Trong đó:

  • Tỉnh có số lượng khách hàng nhiều nhất là WA với 4567 người chiếm tỉ lệ 32.5%,

  • Tiếp theo là CA, OR và Zacatecas với tỉ lệ lần lượt là 19.4%, 16.1%, 9.2%.

  • Các tỉnh còn lại chiếm tỷ lệ nhỏ hơn, dao động từ 0.5% đến 5.8%.

7. Country

# Lập bảng tần số
quocgia <- table(dt$Country)
quocgia
## 
## Canada Mexico    USA 
##    809   3688   9562
# Lập bảng tần suất
table(dt$Country)/sum(nrow(dt))
## 
##     Canada     Mexico        USA 
## 0.05754321 0.26232307 0.68013372
library(ggplot2)
# Bước 1: Đưa về data.frame
df_quocgia <- as.data.frame(quocgia)
colnames(df_quocgia) <- c("Country", "Count")

# Bước 2: Tính phần trăm và nhãn
df_quocgia$Percent <- round(df_quocgia$Count / sum(df_quocgia$Count) * 100, 1)
df_quocgia$Label <- paste0(df_quocgia$Percent, "%")

# Bước 3: Vẽ biểu đồ tròn
ggplot(df_quocgia, aes(x = "", y = Count, fill = Country)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
  labs(title = "Biểu đồ 7: Biểu đồ thể hiện nơi cư trú theo quốc gia") +
  theme_void() +
  scale_fill_manual(
    values = c("beige","darkgreen","darkred")
  ) +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

Vậy trong bộ dữ liệu này có

  • 809 khách hàng đến từ quốc gia Canada chiếm tỉ lệ 5.8%,
  • 3688 khách hàng đến từ quốc gia Mexico chiếm tỉ lệ 26.2%,
  • 9562 khách hàng đến từ quốc gia USA chiếm tỉ lệ 68%.

8. ProductFamily

# Lập bảng tần số
family <- table(dt$ProductFamily)
family
## 
##          Drink           Food Non-Consumable 
##           1250          10153           2656
# Lập bảng tần suất
table(dt$ProductFamily)/sum(nrow(dt))
## 
##          Drink           Food Non-Consumable 
##     0.08891102     0.72217085     0.18891813
# Tạo bảng và đưa về dạng data.frame
df_family <- as.data.frame(family)
colnames(df_family) <- c("ProductFamily", "Count")

# Tính phần trăm và nhãn
df_family$Percent <- round(df_family$Count / sum(df_family$Count) * 100, 1)
df_family$Label <- paste0(df_family$Percent, "%")

# Vẽ biểu đồ tròn
ggplot(df_family, aes(x = "", y = Count, fill = ProductFamily)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
  labs(title = "Biểu đồ 8: Phân bố nhóm sản phẩm") +
  theme_void() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

Vậy trong bộ dữ liệu này có

  • 1250 khách hàng mua sản phẩm thuộc nhóm chiếm tỉ lệ 8.9%,
  • 10153 khách hàng mua sản phẩm thuộc nhóm chiếm tỉ lệ 72.2%,
  • 2656 khách hàng mua sản phẩm thuộc nhóm chiếm tỉ lệ 18.9%.

9. ProductDepartment

# Lập bảng tần số
department <- table(dt$ProductDepartment)
department
## 
## Alcoholic Beverages         Baked Goods        Baking Goods           Beverages 
##                 356                 425                1072                 680 
##     Breakfast Foods        Canned Foods     Canned Products            Carousel 
##                 188                 977                 109                  59 
##            Checkout               Dairy                Deli                Eggs 
##                  82                 903                 699                 198 
##        Frozen Foods  Health and Hygiene           Household                Meat 
##                1382                 893                1420                  89 
##         Periodicals             Produce             Seafood         Snack Foods 
##                 202                1994                 102                1600 
##              Snacks       Starchy Foods 
##                 352                 277
# Lập bảng tần suất
table(dt$ProductDepartment)/sum(nrow(dt))
## 
## Alcoholic Beverages         Baked Goods        Baking Goods           Beverages 
##         0.025321858         0.030229746         0.076250089         0.048367594 
##     Breakfast Foods        Canned Foods     Canned Products            Carousel 
##         0.013372217         0.069492852         0.007753041         0.004196600 
##            Checkout               Dairy                Deli                Eggs 
##         0.005832563         0.064229319         0.049719041         0.014083505 
##        Frozen Foods  Health and Hygiene           Household                Meat 
##         0.098300021         0.063518031         0.101002916         0.006330464 
##         Periodicals             Produce             Seafood         Snack Foods 
##         0.014368020         0.141830856         0.007255139         0.113806103 
##              Snacks       Starchy Foods 
##         0.025037343         0.019702682
library(ggplot2)

# Chuyển sang dạng data frame
df_department <- as.data.frame(department)
colnames(df_department) <- c("ProductDepartment", "Count")

df_department$Percent <- round(df_department$Count / sum(df_department$Count) * 100, 1)
df_department$Label <- paste0(df_department$Percent, "%")

# Vẽ biểu đồ cột
ggplot(df_department, aes(x = ProductDepartment, y = Count, fill = ProductDepartment)) +
  geom_col(width = 0.8) +
  geom_text(aes(label = Count), vjust = -0.5) +
  labs(title = "Biểu đồ 9: Biểu đồ phân bố bộ phận sản phẩm",
       x = "Các bộ phận sản phẩm", 
       y = "Số người",
       fill = "ProductDepartment") + 
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    axis.text.x = element_blank(), # Ẩn nhãn dưới trục X nếu cần
    legend.position = "bottom"
  )

Trong bộ dữ liệu này có tổng cộng 22 bộ phận sản phẩm. Trong đó:

🔶 Nhóm bộ phận sản phẩm có số lượng khách hàng nhiều nhất gồm:

Produce với 1994 người chiếm tỷ lệ 14.2%,

Snack Foods với 1600 người chiếm tỷ lệ 11.4%.

🟠 Nhóm bộ phận sản phẩm có số lượng khách hàng trung bình gồm:

Household,Frozen Foods, Baking Goods, Canned Foods, Dairy, Health and Hygiene với tỉ lệ giao động từ 6.4% đến 10.1%.

🟢 Nhóm bộ phận sản phẩm có số lượng khách hàng ít nhất gồm:

Các bộ phận sản phẩm còn lại với số khách hàng rất ít giao động ở mức 0.4% đến 4.8%.

10. ProductCategory

# Lập bảng tần số
category <- table(dt$ProductCategory)
category
## 
##         Baking Goods    Bathroom Products        Beer and Wine 
##                  484                  365                  356 
##                Bread      Breakfast Foods              Candles 
##                  425                  417                   45 
##                Candy     Canned Anchovies         Canned Clams 
##                  352                   44                   53 
##       Canned Oysters      Canned Sardines        Canned Shrimp 
##                   35                   40                   38 
##          Canned Soup          Canned Tuna Carbonated Beverages 
##                  404                   87                  154 
##    Cleaning Supplies        Cold Remedies                Dairy 
##                  189                   93                  903 
##        Decongestants               Drinks                 Eggs 
##                   85                  135                  198 
##           Electrical      Frozen Desserts       Frozen Entrees 
##                  355                  323                  118 
##                Fruit             Hardware        Hot Beverages 
##                  765                  129                  226 
##              Hygiene     Jams and Jellies     Kitchen Products 
##                  197                  588                  217 
##            Magazines                 Meat        Miscellaneous 
##                  202                  761                   42 
##  Packaged Vegetables       Pain Relievers       Paper Products 
##                   48                  192                  345 
##                Pizza     Plastic Products Pure Juice Beverages 
##                  194                  141                  165 
##              Seafood          Side Dishes          Snack Foods 
##                  102                  153                 1600 
##            Specialty        Starchy Foods           Vegetables 
##                  289                  277                 1728
# Lập bảng tần suất
table(dt$ProductCategory)/sum(nrow(dt))
## 
##         Baking Goods    Bathroom Products        Beer and Wine 
##          0.034426346          0.025962017          0.025321858 
##                Bread      Breakfast Foods              Candles 
##          0.030229746          0.029660716          0.003200797 
##                Candy     Canned Anchovies         Canned Clams 
##          0.025037343          0.003129668          0.003769827 
##       Canned Oysters      Canned Sardines        Canned Shrimp 
##          0.002489508          0.002845153          0.002702895 
##          Canned Soup          Canned Tuna Carbonated Beverages 
##          0.028736041          0.006188207          0.010953837 
##    Cleaning Supplies        Cold Remedies                Dairy 
##          0.013443346          0.006614980          0.064229319 
##        Decongestants               Drinks                 Eggs 
##          0.006045949          0.009602390          0.014083505 
##           Electrical      Frozen Desserts       Frozen Entrees 
##          0.025250729          0.022974607          0.008393200 
##                Fruit             Hardware        Hot Beverages 
##          0.054413543          0.009175617          0.016075112 
##              Hygiene     Jams and Jellies     Kitchen Products 
##          0.014012376          0.041823743          0.015434953 
##            Magazines                 Meat        Miscellaneous 
##          0.014368020          0.054129028          0.002987410 
##  Packaged Vegetables       Pain Relievers       Paper Products 
##          0.003414183          0.013656732          0.024539441 
##                Pizza     Plastic Products Pure Juice Beverages 
##          0.013798990          0.010029163          0.011736254 
##              Seafood          Side Dishes          Snack Foods 
##          0.007255139          0.010882709          0.113806103 
##            Specialty        Starchy Foods           Vegetables 
##          0.020556227          0.019702682          0.122910591
library(ggplot2)

# Đưa dữ liệu về dạng data frame
df_category <- as.data.frame(category)
colnames(df_category) <- c("ProductCategory", "Count")

# Vẽ biểu đồ với nhiều màu
ggplot(df_category, aes(x = reorder(ProductCategory, Count), y = Count, fill = ProductCategory)) +
  geom_col() +
  coord_flip() +
  labs(title = "Biểu đồ 10: Tần suất giao dịch theo Product Category",
       x = "Loại sản phẩm",
       y = "Số giao dịch") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    legend.position = "none"  # Ẩn chú thích màu nếu không cần
  )

Phần 3: Ước lượng Khoảng và Kiểm định Giả thuyết cho Tỷ lệ (Một biến)

3.1. Gender (Female)

Kiểm định giả thuyết: H0: tỷ lệ Female = 0.5

H1: tỷ lệ Female ≠ 0.5

# Lấy số lượng Female và tổng số quan sát
count_female <- gioitinh["F"]
n <- sum(gioitinh)

# 1. Ước lượng khoảng tin cậy 95% cho tỷ lệ Female
prop.test_female <- prop.test(count_female, n, conf.level = 0.95)

# Kết quả khoảng tin cậy
cat("Khoảng tin cậy 95% cho tỷ lệ Female là: ",
    round(prop.test_female$conf.int[1],3), " đến ", round(prop.test_female$conf.int[2],3), "\n")
## Khoảng tin cậy 95% cho tỷ lệ Female là:  0.502  đến  0.518
# 2. Kiểm định giả thuyết H0: p = 0.5
# prop.test cũng trả luôn p-value
cat("Giá trị p của kiểm định là: ", prop.test_female$p.value, "\n")
## Giá trị p của kiểm định là:  0.01820308
if (prop.test_female$p.value < 0.05) {
  cat("=> Bác bỏ H0, tỷ lệ Female khác 0.5 với mức ý nghĩa 0.05\n")
} else {
  cat("=> Không bác bỏ H0, không có đủ bằng chứng để khẳng định tỷ lệ Female khác 0.5\n")
}
## => Bác bỏ H0, tỷ lệ Female khác 0.5 với mức ý nghĩa 0.05

3.2. Homeowner

# Lấy số người có nhà ("Y")
count_yes <- nha["Y"]
n_home <- sum(nha)

# Tính khoảng tin cậy 95%
prop.test_yes  <- prop.test(count_yes, n_home, conf.level = 0.95)

# Kết quả khoảng tin cậy
cat("Khoảng tin cậy 95% cho tỷ lệ người có nhà:\n")
## Khoảng tin cậy 95% cho tỷ lệ người có nhà:
cat("Từ", round(prop.test_yes$conf.int[1], 3), "đến", round(prop.test_yes$conf.int[2], 3), "\n")
## Từ 0.592 đến 0.609
cat("Tỷ lệ ước lượng:", round(prop.test_yes$estimate, 3), "\n\n")
## Tỷ lệ ước lượng: 0.601
# Kiểm định H₀: p = 0.5 (tỷ lệ người có nhà bằng 50%)
test_home <- prop.test(count_yes, n_home, p = 0.5, conf.level = 0.95)

# Kết quả kiểm định
cat("Kết quả kiểm định giả thuyết H0: tỷ lệ người có nhà = 0.5\n")
## Kết quả kiểm định giả thuyết H0: tỷ lệ người có nhà = 0.5
cat("Giá trị p-value:", round(test_home$p.value, 4), "\n")
## Giá trị p-value: 0
if (test_home$p.value < 0.05) {
  cat("=> Bác bỏ H0: Tỷ lệ người có nhà khác 0.5 (có ý nghĩa thống kê ở mức 5%)\n")
} else {
  cat("=> Không bác bỏ H0: Không có đủ bằng chứng để kết luận tỷ lệ người có nhà khác 0.5\n")
}
## => Bác bỏ H0: Tỷ lệ người có nhà khác 0.5 (có ý nghĩa thống kê ở mức 5%)

3.3. MaritalStatus

count_married <- honnhan["M"]
n_marital <- sum(honnhan)


prop.test_married <- prop.test(count_married, n_marital, conf.level = 0.95)

cat("Khoảng tin cậy 95% cho tỷ lệ người đã kết hôn:\n")
## Khoảng tin cậy 95% cho tỷ lệ người đã kết hôn:
cat("Từ", round(prop.test_married$conf.int[1], 3), "đến", round(prop.test_married$conf.int[2], 3), "\n")
## Từ 0.48 đến 0.497
cat("Tỷ lệ ước lượng:", round(prop.test_married$estimate, 3), "\n\n")
## Tỷ lệ ước lượng: 0.488
test_married <- prop.test(count_married, n_marital, p = 0.5, conf.level = 0.95)

cat("Kết quả kiểm định 
giả thuyết H0: tỷ lệ người đã kết hôn = 0.5\n",
    "giả thuyết H1: tỷ lệ người đã kết hôn ≠ 0.5\n")
## Kết quả kiểm định 
## giả thuyết H0: tỷ lệ người đã kết hôn = 0.5
##  giả thuyết H1: tỷ lệ người đã kết hôn ≠ 0.5
cat("Giá trị p-value:", round(test_married$p.value, 4), "\n")
## Giá trị p-value: 0.006
if (test_married$p.value < 0.05) {
  cat("=> Bác bỏ H0: Tỷ lệ người đã kết hôn khác 0.5 (có ý nghĩa thống kê ở mức 5%)\n")
} else {
  cat("=> Không bác bỏ H0: Không có đủ bằng chứng để kết luận tỷ lệ người đã kết hôn khác 0.5\n")
}
## => Bác bỏ H0: Tỷ lệ người đã kết hôn khác 0.5 (có ý nghĩa thống kê ở mức 5%)

Phần 4: Phân tích mối quan hệ giữa hai biến Định tính

4.1. Gender và Homeowner

4.1.1. Thống kê mô tả

# Bảng tần suất chéo giữa Gender và Homeowner
table_gender_home <- table(dt$Gender, dt$Homeowner)
table_gender_home
##    
##        N    Y
##   F 2826 4344
##   M 2789 4100
# Tỷ lệ phần trăm theo hàng (trong từng giới tính, có nhà chiếm bao nhiêu %)
prop_gender_home_row <- prop.table(table_gender_home, 1) * 100
round(prop_gender_home_row, 2)
##    
##         N     Y
##   F 39.41 60.59
##   M 40.48 59.52
diff_count <- table_gender_home["F", "Y"] - table_gender_home["M", "Y"]
diff_not_owner_count <-table_gender_home["F", "N"]- table_gender_home["M", "N"]

cat("Nữ sở hữu nhà nhiều hơn nam:", diff_count, "người")
## Nữ sở hữu nhà nhiều hơn nam: 244 người
cat("Nữ không sở hữu nhà nhiều hơn nam:", diff_not_owner_count, "người\n")
## Nữ không sở hữu nhà nhiều hơn nam: 37 người
library(ggplot2)

# Chuyển bảng sang data frame
df_gender_home <- as.data.frame(table_gender_home)
colnames(df_gender_home) <- c("Gender", "Homeowner", "Count")

# Vẽ biểu đồ cột nhóm
ggplot(df_gender_home, aes(x = Gender, y = Count, fill = Homeowner)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Mối quan hệ giữa Gender và Homeowner",
       x = "Gender", y = "Số lượng", fill = "Homeowner") +
  theme_minimal()

Nhận xét:

  • Trong bộ dữ liệu, có 2826 nữ không sở hữu nhà và 4344 nữ đã có nhà, 2789 nam không sở hữu nhà và 4100 nam đã có nhà.

  • Số người nữ có nhà nhiều hơn số người nam có nhà bằng 244 người.

  • Số người nữ không có nhà nhiều hơn số người nam không có nhà bằng 37 người.

4.1.2. Kiểm định thống kê

Giả thuyết kiểm định:

  • H₀: Giới tính và việc sở hữu nhà không ảnh hướng đến nhau.

  • H₁: Giới tính và việc sở hữu nhà có liên quan đến nhau.

chi_gender_home <- chisq.test(table_gender_home)
chi_gender_home
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  table_gender_home
## X-squared = 1.6344, df = 1, p-value = 0.2011
cat("Giá trị Chi-squared:", round(chi_gender_home$statistic, 2), "\n")
## Giá trị Chi-squared: 1.63
cat("Bậc tự do:", chi_gender_home$parameter, "\n")
## Bậc tự do: 1
cat("P-value:", round(chi_gender_home$p.value, 4), "\n")
## P-value: 0.2011
if (chi_gender_home$p.value < 0.05) {
  cat("=> Bác bỏ H0: Có mối liên hệ có ý nghĩa thống kê giữa Gender và Homeowner\n")
} else {
  cat("=> Không bác bỏ H0: Không có bằng chứng thống kê về mối liên hệ giữa Gender và Homeowner\n")
}
## => Không bác bỏ H0: Không có bằng chứng thống kê về mối liên hệ giữa Gender và Homeowner

4.2. MaritalStatus và Homeowner

4.2.1. Thống kê mô tả

# Bảng tần suất chéo giữa MaritalStatus và Homeowner
table_marital_home <- table(dt$MaritalStatus, dt$Homeowner)
table_marital_home
##    
##        N    Y
##   M 1719 5147
##   S 3896 3297
# Tỷ lệ phần trăm theo hàng
prop_marital_home_row <- prop.table(table_marital_home, 1) * 100
round(prop_marital_home_row, 2)
##    
##         N     Y
##   M 25.04 74.96
##   S 54.16 45.84
# Tính chênh lệch số lượng sở hữu nhà và không sở hữu nhà
diff_owner_count <- table_marital_home["M", "Y"] - table_marital_home["S", "Y"]
diff_not_owner_count <- table_marital_home["S", "N"] - table_marital_home["M", "N"]

# In kết quả
cat("Người đã kết hôn sở hữu nhà nhiều hơn người độc thân:", diff_owner_count, "người\n")
## Người đã kết hôn sở hữu nhà nhiều hơn người độc thân: 1850 người
cat("Người độc thân không sở hữu nhà nhiều hơn người đã kết hôn:", diff_not_owner_count, "người\n")
## Người độc thân không sở hữu nhà nhiều hơn người đã kết hôn: 2177 người
library(ggplot2)

# Chuyển sang data frame
df_marital_home <- as.data.frame(table_marital_home)
colnames(df_marital_home) <- c("MaritalStatus", "Homeowner", "Count")

# Vẽ biểu đồ
ggplot(df_marital_home, aes(x = MaritalStatus, y = Count, fill = Homeowner)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Mối quan hệ giữa MaritalStatus và Homeowner",
       x = "Tình trạng hôn nhân", y = "Số lượng", fill = "Có nhà") +
  theme_minimal()

Nhận xét:

  • 74.96% người đã kết hôn sở hữu nhà, trong khi chỉ có 45.84% người độc thân sở hữu nhà với số người chênh lệch là 1850 người.

  • Ngược lại, 54.16% người độc thân không sở hữu nhà, trong khi tỷ lệ này ở người đã kết hôn chỉ là 25.04%, chênh lệch nhau 2177 người.

  • Sự chênh lệch này cho thấy người đã kết hôn có xu hướng sở hữu nhà nhiều hơn người độc thân.

4.2.2. Kiểm định thống kê

Giả thuyết kiểm định:

  • H₀: Việc kết hôn và việc sở hữu nhà không liên quan đến nhau.

  • H₁: Việc kết hôn và việc sở hữu nhà có liên quan đến nhau.

chi_marital_home <- chisq.test(table_marital_home)
chi_marital_home
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  table_marital_home
## X-squared = 1241.2, df = 1, p-value < 2.2e-16
cat("Giá trị Chi-squared:", round(chi_marital_home$statistic, 2), "\n")
## Giá trị Chi-squared: 1241.22
cat("Bậc tự do:", chi_marital_home$parameter, "\n")
## Bậc tự do: 1
cat("P-value:", round(chi_marital_home$p.value, 4), "\n")
## P-value: 0
if (chi_marital_home$p.value < 0.05) {
  cat("=> Bác bỏ H₀: Có mối liên hệ có ý nghĩa thống kê giữa MaritalStatus và Homeowner\n")
} else {
  cat("=> Không bác bỏ H₀: Không có bằng chứng thống kê về mối liên hệ giữa MaritalStatus và Homeowner\n")
}
## => Bác bỏ H₀: Có mối liên hệ có ý nghĩa thống kê giữa MaritalStatus và Homeowner

4.3. Gender và MaritalStatus

4.3.1. Thống kê mô tả

# Tạo bảng tần suất chéo giữa Gender và MaritalStatus
table_gender_marital <- table(dt$Gender, dt$MaritalStatus)
table_gender_marital
##    
##        M    S
##   F 3602 3568
##   M 3264 3625
# Theo hàng: trong từng giới tính, tỉ lệ từng tình trạng hôn nhân
prop_gender_marital_row <- prop.table(table_gender_marital, 1) * 100
round(prop_gender_marital_row, 2)
##    
##         M     S
##   F 50.24 49.76
##   M 47.38 52.62
library(ggplot2)

df_gender_marital <- as.data.frame(table_gender_marital)
colnames(df_gender_marital) <- c("Gender", "MaritalStatus", "Count")

ggplot(df_gender_marital, aes(x = Gender, y = Count, fill = MaritalStatus)) +
  geom_bar(stat = "identity", position = "fill") +  # hoặc "dodge" nếu muốn số lượng
  labs(title = "Tỷ lệ MaritalStatus theo Gender",
       x = "Gender", y = "Tỷ lệ", fill = "Tình trạng hôn nhân") +
  scale_y_continuous(labels = scales::percent) +
  theme_minimal()

4.3.2. Kiểm định thống kê

Giả thuyết kiểm định:

  • H₀: Việc kết hôn và giới tính không liên quan đến nhau.

  • H₁: Việc kết hôn và giới tính có liên quan đến nhau.

chi_gender_marital <- chisq.test(table_gender_marital)
chi_gender_marital
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  table_gender_marital
## X-squared = 11.365, df = 1, p-value = 0.0007485
cat("Giá trị Chi-squared:", round(chi_gender_marital$statistic, 2), "\n")
## Giá trị Chi-squared: 11.36
cat("Bậc tự do:", chi_gender_marital$parameter, "\n")
## Bậc tự do: 1
cat("P-value:", round(chi_gender_marital$p.value, 4), "\n")
## P-value: 7e-04
if (chi_gender_marital$p.value < 0.05) {
  cat("=> Bác bỏ H₀: Có mối liên hệ có ý nghĩa thống kê giữa Gender và MaritalStatus\n")
} else {
  cat("=> Không bác bỏ H₀: Không có bằng chứng thống kê về mối liên hệ giữa Gender và MaritalStatus\n")
}
## => Bác bỏ H₀: Có mối liên hệ có ý nghĩa thống kê giữa Gender và MaritalStatus

Phần 5: Tổng kết và Thảo luận

    Về giới tính và sở hữu nhà :
  • Tỷ lệ phụ nữ sở hữu nhà cao hơn nam giới (60.59% so với 59.52%).

  • Số lượng nữ sở hữu nhà nhiều hơn nam giới 244 người, cho thấy phụ nữ trong tập dữ liệu này có xu hướng ổn định hơn về chỗ ở.

    Về tình trạng hôn nhân và sở hữu nhà:
  • Tỷ lệ người đã kết hôn sở hữu nhà cao vượt trội (74.96%) so với người độc thân (45.84%).

  • Người đã kết hôn sở hữu nhà nhiều hơn người độc thân 1850 người cho thấy sự ổn định tài chính cao hơn ở nhóm đã lập gia đình.

    Về mối quan hệ giữa giới tính và tình trạng hôn nhân:
  • Không có sự khác biệt rõ rệt về tỷ lệ kết hôn giữa nam và nữ.

  • Phân tích thống kê cho thấy mối quan hệ giữa giới tính và tình trạng hôn nhân là có ý nghĩa thống kê (p-value rất nhỏ).

=> Điều này gợi ý rằng doanh nghiệp có thể cân nhắc chiến lược phân khúc thị trường theo giới tính và tình trạng hôn nhân để tối ưu hóa tiếp cận sản phẩm.

    Hạn chế của phân tích
  • Chỉ dùng biến định tính nên không thể xem xét ảnh hưởng của các yếu tố định lượng như thu nhập (AnnualIncome) hay độ tuổi (Age) – vốn có thể ảnh hưởng mạnh đến hành vi mua sắm.

  • Một số nhóm có số lượng nhỏ, ví dụ một số hạng mục trong ProductCategory hoặc MaritalStatus, khiến phân tích thống kê dễ bị lệch.

  • Không xét đến hành vi theo thời gian (seasonality, xu hướng), hoặc loại sản phẩm cụ thể mà khách hàng quan tâm.

    Đề xuất cho doanh nghiệp
  • Nhắm đến phụ nữ đã kết hôn như một nhóm khách hàng tiềm năng cao – ổn định về tài chính và có khả năng sở hữu nhà cao → có thể mua các sản phẩm cao cấp, định cư.

  • Người độc thân có thể phù hợp với các chiến lược tiếp thị linh hoạt hơn, tập trung vào trải nghiệm hoặc dịch vụ thuê.

  • Các sản phẩm liên quan đến nhà cửa, gia đình nên ưu tiên cho nhóm đã kết hôn.

  • Với nam giới, cần nội dung tiếp thị cụ thể hơn do tỷ lệ sở hữu nhà và sự ổn định có xu hướng thấp hơn.

# Gán biến số lượng người từ Canada và Mexico
canada_count <- quocgia["Canada"]
mexico_count <- quocgia["Mexico"]

# Tổng số người từ mỗi quốc gia (ở đây cũng là số lượng từng nhóm)
n_canada <- canada_count
n_mexico <- mexico_count

# Kiểm định prop.test với biến
prop.test(x = c(canada_count, mexico_count),
          n = c(n_canada, n_mexico),
          alternative = "less")
## Warning in prop.test(x = c(canada_count, mexico_count), n = c(n_canada, :
## Chi-squared approximation may be incorrect
## 
##  2-sample test for equality of proportions without continuity correction
## 
## data:  c(canada_count, mexico_count) out of c(n_canada, n_mexico)
## X-squared = NaN, df = 1, p-value = NA
## alternative hypothesis: less
## 95 percent confidence interval:
##  -1  0
## sample estimates:
## prop 1 prop 2 
##      1      1

Phần 6: NHIỆM VỤ TUẦN 3

6.1. Bảng ngẫu nhiên

Bảng ngẫu nhiên (random table) là một bảng hoặc ma trận trong đó mỗi ô chứa một giá trị được sinh ra từ một biến ngẫu nhiên theo một phân phối xác định. Bảng này có thể được dùng để mô phỏng, mô hình hóa các hiện tượng ngẫu nhiên trong tự nhiên, xã hội hoặc kỹ thuật.

  • Phân phối Multinomial mở rộng của phân phối nhị thức (Binomial), dùng để mô hình hóa xác suất của các kết quả rời rạc từ một thử nghiệm lặp đi lặp lại.

  • Phân phối Poisson được dùng để mô tả số lần xảy ra của một sự kiện trong một khoảng thời gian hoặc không gian nhất định khi các sự kiện xảy ra độc lập với nhau.

Bảng ngẫu nhiên (contingency table) là công cụ thống kê dùng để trình bày tần số của các biến phân loại. Trong trường hợp đơn giản nhất – bảng 2x2 – bảng có dạng:

cat("
              Biến B = Yes    Biến B = No
Biến A = Yes       a              b
Biến A = No        c              d
")
## 
##               Biến B = Yes    Biến B = No
## Biến A = Yes       a              b
## Biến A = No        c              d

6.1.1. Phân phối Poisson

Phân phối Poisson là một phân phối xác suất rời rạc mô tả số lần xảy ra của một sự kiện trong một khoảng thời gian hoặc không gian nhất định, khi các sự kiện xảy ra độc lập và với tốc độ xảy ra trung bình không đổi. \[ X_{ij} \sim \text{Poisson}(\lambda_{ij}) \] Trong đó:

X: số sự kiện xảy ra trong khoảng thời gian hoặc không gian đã xác định

\(\lambda > 0\): số lần xảy ra trung bình trong 1 đơn vị thời gian hoặc không gian

Hàm xác suất (PMF):

\[ P(X = k) = \frac{e^{-\lambda} \lambda^k}{k!}, \quad \text{với } k = 0, 1, 2, \dots \]

6.1.2. Phân phối Binomial

Phân phối đa thức mở rộng của phân phối nhị thức (Binomial) cho trường hợp có nhiều hơn 2 kết quả có thể xảy ra trong mỗi thử nghiệm độc lập.

Hàm xác suất

\[ P(X_1 = x_1, \dots, X_k = x_k) = \frac{n!}{x_1! x_2! \cdots x_k!} \cdot p_1^{x_1} p_2^{x_2} \cdots p_k^{x_k} \]

Trong đó:

\(X_i \text{ là số lần kết quả thứ } i \text{ xuất hiện trong } n \text{ thử}\)

\(x_i \ge 0 \quad \text{và} \quad \sum_{i=1}^{k} x_i = n\)

6.2. Tỷ lệ trong bảng ngẫu nhiên 2x2:

6.2.1. Hiệu hai tỷ lệ (Risk Difference - RD)

\[ RD = \hat{p}_1 - \hat{p}_2 = \frac{a + b}{a} - \frac{c + d}{c} \] Trong đó:

  • \(\frac{a}{a + b} \text{ là tỷ lệ xảy ra sự kiện trong nhóm A = Yes.}\)

  • \(\frac{c}{c + d} \text{ là tỷ lệ xảy ra sự kiện trong nhóm A = No.}\)

6.2.2. Tỷ số nguy cơ (Relative Risk - RR)

\[ RR = \frac{\hat{p}_2}{\hat{p}_1} = \frac{\frac{c}{c + d}}{\frac{a}{a + b}} \]

Ý nghĩa

Tỷ số nguy cơ đo lường mức độ tăng (hoặc giảm) nguy cơ xảy ra sự kiện ở nhóm A = Yes so với nhóm A = No. Thể hiện mức tăng hoặc giảm tương đối của nguy cơ xảy ra sự kiện trong một nhóm so với nhóm còn lại.

  • Nếu RR=1: Không có sự khác biệt nguy cơ.

  • Nếu RR>1: Nguy cơ ở nhóm A = Yes cao hơn nhóm A = No.

  • Nếu RR<1: Nguy cơ ở nhóm A = Yes thấp hơn nhóm A = No.

6.2.3. Tỷ số chênh (Odds Ratio - OR)

\[ OR = \frac{b \times c}{a \times d} \] Ý nghĩa

Tỷ số chênh đo lường tỷ lệ odds xảy ra sự kiện ở nhóm A = Yes so với nhóm A = No. Thể hiện tỷ số giữa xác suất xảy ra và không xảy ra của một sự kiện trong mỗi nhóm.

  • Nếu OR=1: Không có sự khác biệt odds.

  • Nếu OR>1: Odds sự kiện cao hơn ở nhóm A = Yes.

  • Nếu OR<1: Odds sự kiện thấp hơn ở nhóm A = Yes.

6.3. Khoảng tin cậy

6.3.1. Relative Risk

Tính log(RR)

\[ \log(RR) = \log\left(\frac{\frac{c}{c + d}}{\frac{a}{a + b}}\right) \]

Tính sai số chuẩn (SE)

\[ \log(RR) = \log\left(\frac{\frac{c}{c + d}}{\frac{a}{a + b}}\right) \]

Tính khoảng tin cậy cho log(RR)

\[ \log(RR) \pm Z_{1 - \alpha/2} \cdot SE[\log(RR)] \]

Lấy mũ để trở lại RR

\[ CI_{RR} = \left[ \exp(\log(RR) - 1.96 \cdot SE),\ \exp(\log(RR) + 1.96 \cdot SE) \right] \] Với mức tin cậy 95%, thì \(Z_{1 - \alpha/2} = 1.96\)

6.3.2. Odds Ratio

Tính log(OR)

\[ \log(OR)=\log\left(\frac{b \cdot c}{a \cdot d}\right) \]

Tính sai số chuẩn (SE)

\[ SE[\log(OR)] = \sqrt{\frac{1}{a} + \frac{1}{b} + \frac{1}{c} + \frac{1}{d}} \]

Tính khoảng tin cậy cho log(OR)

\[ \log(RR) \pm Z_{1 - \alpha/2} \cdot SE[\log(RR)] \]

Lấy mũ để trở lại OR

\[ CI_{OR} = \left[ \exp\big(\log(OR) - 1.96 \times SE\big), \quad \exp\big(\log(OR) + 1.96 \times SE\big) \right] \]

Với mức tin cậy 95%, thì \(Z_{1 - \alpha/2} = 1.96\)

6.4. Gender và Homeowner

# Bảng chéo giữa Gender và Homeowner
cheo <- table(dt$Gender, dt$Homeowner)
addmargins(cheo)
##      
##           N     Y   Sum
##   F    2826  4344  7170
##   M    2789  4100  6889
##   Sum  5615  8444 14059

\(p_1=P(Homeowner=Y∣Gender=F)(tỷ lệ nữ sở hữu nhà)\)

\(p_2=P(Homeowner=Y∣Gender=M)(tỷ lệ nam sở hữu nhà)\)

Tính hiệu hai tỷ lệ:

\[d=p_1−p_2\] Kiểm định:

H₀: p₁ - p₂ = 0 (tỷ lệ nữ sở hữu nhà bằng tỷ lệ nam sở hữu nhà)

H₁: p₁ - p₂ < 0 (Tỷ lệ nữ sở hữu nhà nhỏ hơn tỷ lệ nam sở hữu nhà )

counts <- c(cheo["F", "Y"], cheo["M", "Y"])   # Số người sở hữu nhà theo từng giới tính
totals <- c(sum(cheo["F", ]), sum(cheo["M", ]))  # Tổng số người theo từng giới tính

test <- prop.test(counts, totals, alternative = "less", correct = FALSE)
test
## 
##  2-sample test for equality of proportions without continuity correction
## 
## data:  counts out of totals
## X-squared = 1.6788, df = 1, p-value = 0.9025
## alternative hypothesis: less
## 95 percent confidence interval:
##  -1.00000000  0.02429777
## sample estimates:
##    prop 1    prop 2 
## 0.6058577 0.5951517
  • prop 1 (p1) = 0.6059: tỷ lệ nữ sở hữu nhà khoảng 60.59%

  • prop 2 (p2) = 0.5952: tỷ lệ nam sở hữu nhà khoảng 59.52%

  • P-value = 0.9025

Với giá trị p-value 0.9025, lớn hơn mức ý nghĩa 0.05. Do đó, không đủ bằng chứng để bác bỏ giả thuyết H₀

Điều này có nghĩa là, không có sự khác biệt có ý nghĩa thống kê giữa tỷ lệ sở hữu nhà của nữ và nam, và không có bằng chứng cho thấy tỷ lệ nữ sở hữu nhà thấp hơn tỷ lệ nam.

LS0tDQp0aXRsZTogIk5oaeG7h20gduG7pSB0deG6p24gMiINCmF1dGhvcjogIkh14buzbmggTmfhu41jIERp4buHcC0yMjIxMDAwMjk1Ig0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUg6JU06JVMsICVkIC0gJW0gLSAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICBwZGZfZG9jdW1lbnQ6DQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4DQotLS0NCg0KDQo8c3R5bGU+DQpib2R5IHsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBzYW5zLXNlcmlmOw0KICBmb250LXNpemU6IDE2cHg7DQogIHRleHQtYWxpZ246IGp1c3RpZnk7DQogIGxpbmUtaGVpZ2h0OiAxLjU7DQp9DQpoMSB7DQogIGNvbG9yOiByZWQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7DQogIGNvbG9yOiBibGFjazsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMyB7DQogIGNvbG9yOiBibGFjazsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoNCB7DQogIGNvbG9yOiBibGFjazsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQo8L3N0eWxlPg0KDQojIyBQaOG6p24gMTogVMOsbSBoaeG7g3UgdsOgIENodeG6qW4gYuG7iyBk4buvIGxp4buHdQ0KYGBge3J9DQojIMSQ4buNYyBi4buZIGThu68gbGnhu4d1DQpkIDwtIHJlYWQuY3N2KCJEOi9uYWFhYWFhL1BURExEVC9TdXBlcm1hcmtldCBUcmFuc2FjdGlvbnMuY3N2IiwgaGVhZGVyID0gVCkNCmBgYA0KIyMjIDEuIEPhuqV1IHRyw7pjIGLhu5kgZOG7ryBsaeG7h3UNCmBgYHtyfQ0KIyBD4bqldSB0csO6YyBi4buZIGThu68gbGnhu4d1DQpzdHIoZCkNCmBgYA0KDQpU4bqtcCBk4buvIGxp4buHdSAiU3VwZXJtYXJrZXQgVHJhbnNhY3Rpb25zIiBn4buTbSB04buVbmcgY+G7mW5nIDE0LjA1OSBkw7JuZyBk4buvIGxp4buHdSwgdMawxqFuZyDhu6luZyB24bubaSAxNC4wNTkgZ2lhbyBk4buLY2ggbXVhIGjDoG5nIMSRxrDhu6NjIGdoaSBuaOG6rW4gdOG6oWkgc2nDqnUgdGjhu4suIE3hu5dpIGTDsm5nIGThu68gbGnhu4d1IMSR4bqhaSBkaeG7h24gY2hvIG3hu5l0IGdpYW8gZOG7i2NoIHJpw6puZyBs4bq7IHbDoCBiYW8gZ+G7k20gdGjDtG5nIHRpbiBjaGkgdGnhur90IHbhu4Ega2jDoWNoIGjDoG5nLCBz4bqjbiBwaOG6qW0sIGPFqW5nIG5oxrAgY8OhYyDEkeG6t2MgxJFp4buDbSBsacOqbiBxdWFuIMSR4bq/biBow6BuaCB2aSB0acOqdSBkw7luZy4gQuG7mSBk4buvIGxp4buHdSBuw6B5IGPDsyB04buVbmcgY+G7mW5nIDE2IGJp4bq/biAoY+G7mXQpLCBiYW8gZ+G7k20gY+G6oyBiaeG6v24gxJHhu4tuaCB0w61uaCB2w6AgxJHhu4tuaCBsxrDhu6NuZywgcGjhuqNuIMOhbmggxJHhuqd5IMSR4bunIGPDoWMgeeG6v3UgdOG7kSBuaMOibiBraOG6qXUgaOG7jWMsIMSR4buLYSBsw70gdsOgIGvhur90IHF14bqjIGtpbmggZG9hbmggdOG7qyBt4buXaSBnaWFvIGThu4tjaC4NCg0KYGBge3J9DQojIEhp4buHbiB0aOG7iyBjw6FjIGTDsm5nIMSR4bqndSBj4bunYSBi4buZIGThu68gbGnhu4d1DQpoZWFkKGQpDQpgYGANCmBgYHtyfQ0KIyBIaeG7g24gdGjhu4sgY8OhYyBkw7JuZyBjdeG7kWkgY+G7p2EgYuG7mSBk4buvIGxp4buHdQ0KdGFpbChkKQ0KYGBgDQojIyMgMi4gS2nhu4NtIHRyYSBk4buvIGxp4buHdSB0aGnhur91DQoNCmBgYHtyfQ0KIyBLaeG7g20gdHJhIGThu68gbGnhu4d1IHRoaeG6v3UNCnN1bShpcy5uYShkKSkNCmBgYA0KQuG7mSBk4buvIGxp4buHdSBraMO0bmcgY8OzIGThu68gbGnhu4d1IHRoaeG6v3UgDQoNCiMjIyAzLiBDaHV54buDbiBk4bqhbmcgZOG7ryBsaeG7h3UNCmBgYHtyfQ0KZCRHZW5kZXIgPC0gYXMuZmFjdG9yKGQkR2VuZGVyKQ0KZCRNYXJpdGFsU3RhdHVzIDwtIGFzLmZhY3RvcihkJE1hcml0YWxTdGF0dXMpDQpkJEhvbWVvd25lciA8LSBhcy5mYWN0b3IoZCRIb21lb3duZXIpDQpkJEFubnVhbEluY29tZSA8LSBhcy5mYWN0b3IoZCRBbm51YWxJbmNvbWUpDQpgYGANCg0KDQpgYGB7cn0NCiMgVOG6oW8gYmnhur9uIMSR4buLbmggdMOtbmgNCmJkdCA8LSBjKCAiUHVyY2hhc2VEYXRlIiwNCiJDdXN0b21lcklEIiwgIkdlbmRlciIsICJNYXJpdGFsU3RhdHVzIiwgIkhvbWVvd25lciIsICJBbm51YWxJbmNvbWUiLCAiQ2l0eSIsICJTdGF0ZW9yUHJvdmluY2UiLCJDb3VudHJ5IiwgIlByb2R1Y3RGYW1pbHkiLCAiUHJvZHVjdERlcGFydG1lbnQiLCAiUHJvZHVjdENhdGVnb3J5IiApDQoNCiMgVOG6oW8gYuG7mSBk4buvIGxp4buHdSBt4bubaSBjaOG7iSBjw7MgYmnhur9uIMSR4buLbmggdMOtbmgNCmR0IDwtIGRbLCBiZHRdDQoNCiMgWHXhuqV0IGLhu5kgZOG7ryBsaeG7h3UgxJHhu4tuaCB0w61uaA0KaGVhZChkdCkNCg0KYGBgDQojIyBQaOG6p24gMjogIFBow6JuIHTDrWNoIG3DtCB04bqjIG3hu5l0IGJp4bq/biDEkOG7i25oIHTDrW5oIA0KDQojIyMgMS4gR2VuZGVyDQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc+G7kQ0KZ2lvaXRpbmggPC0gdGFibGUoZHQkR2VuZGVyKQ0KZ2lvaXRpbmgNCmBgYA0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHN14bqldA0KdGFibGUoZHQkR2VuZGVyKS9zdW0obnJvdyhkdCkpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgxJDGsGEgduG7gSBk4bqhbmcgZGF0YSBmcmFtZQ0KZGZfZ2lvaXRpbmggPC0gYXMuZGF0YS5mcmFtZShnaW9pdGluaCkNCmNvbG5hbWVzKGRmX2dpb2l0aW5oKSA8LSBjKCJHZW5kZXIiLCAiQ291bnQiKQ0KDQojIFTDrW5oIHBo4bqnbiB0csSDbSB2w6AgdGjDqm0gdsOgbyBkYXRhIGZyYW1lDQpkZl9naW9pdGluaCRQZXJjZW50IDwtIHJvdW5kKGRmX2dpb2l0aW5oJENvdW50IC8gc3VtKGRmX2dpb2l0aW5oJENvdW50KSAqIDEwMCwgMSkNCg0KIyBU4bqhbyBuaMOjbiBnaMOpcCB24bubaSAlDQpkZl9naW9pdGluaCRMYWJlbCA8LSBwYXN0ZTAoZGZfZ2lvaXRpbmgkUGVyY2VudCwgIiUiKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTIHRyw7JuDQpnZ3Bsb3QoZGZfZ2lvaXRpbmgsIGFlcyh4ID0gIiIsIHkgPSBDb3VudCwgZmlsbCA9IEdlbmRlcikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gTGFiZWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyAxOiBCaeG7g3UgxJHhu5MgcGjDom4gYuG7kSBnaeG7m2kgdMOtbmgiKSArICAjIPCfkYggVGnDqnUgxJHhu4ENCiAgdGhlbWVfdm9pZCgpICsgIyBi4buPIMSRaSBjw6FjIHZp4buBbiwgbeG6pXkgY8OhaSBjaMO6IHRow61jaCB4LCB5IMSR4buTIMOhDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIikpICAjIPCfkYggQ2hvIGhp4buHbiB2w6AgY2jhu4luaCB0acOqdSDEkeG7gQ0KYGBgDQoNClbhuq15IHRyb25nIGLhu5kgZOG7ryBsaeG7h3UgbsOgeSBjw7MgYHIgZ2lvaXRpbmhbMV1gIGtow6FjaCBow6BuZyBsw6AgbuG7ryBjaGnhur9tIHThu4kgbOG7hyBgciBkZl9naW9pdGluaCRMYWJlbFsxXWAgdsOgIGByIGdpb2l0aW5oWzJdYCBraMOhY2ggaMOgbmcgbMOgIG5hbSBjaGnhur9tIHThu4kgbOG7hyBgciBkZl9naW9pdGluaCRMYWJlbFsyXWAgLg0KDQoNCiMjIyAyLiBNYXJpYWxTdGF0dXMNCg0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBz4buRDQpob25uaGFuIDwtIHRhYmxlKGR0JE1hcml0YWxTdGF0dXMpDQpob25uaGFuDQoNCmBgYA0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBzdeG6pXQNCnRhYmxlKGR0JE1hcml0YWxTdGF0dXMpL3N1bShucm93KGR0KSkNCmBgYA0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmRmX2hvbm5oYW4gPC0gYXMuZGF0YS5mcmFtZShob25uaGFuKQ0KY29sbmFtZXMoZGZfaG9ubmhhbikgPC0gYygiTWFyaXRhbFN0YXR1cyIsICJDb3VudCIpDQoNCmRmX2hvbm5oYW4kUGVyY2VudCA8LSByb3VuZChkZl9ob25uaGFuJENvdW50IC8gc3VtKGRmX2hvbm5oYW4kQ291bnQpICogMTAwLCAxKQ0KZGZfaG9ubmhhbiRMYWJlbCA8LSBwYXN0ZTAoZGZfaG9ubmhhbiRQZXJjZW50LCAiJSIpDQoNCmdncGxvdChkZl9ob25uaGFuLCBhZXMoeCA9ICIiLCB5ID0gQ291bnQsIGZpbGwgPSBNYXJpdGFsU3RhdHVzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBMYWJlbCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGxhYnModGl0bGUgPSAiQmnhu4N1IMSR4buTIDI6IEJp4buDdSDEkeG7kyBwaMOibiBi4buRIHTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiIpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIk0iID0gInJlZCIsICJTIiA9ICJncmVlbiIpLA0KICAgIGxhYmVscyA9IGMoIk0iID0gIsSQw6Mga+G6v3QgaMO0biIsICJTIiA9ICLEkOG7mWMgdGjDom4iKQ0KICApICsNCiAgdGhlbWUgKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIikpDQoNCg0KYGBgDQoNCg0KVuG6rXkgdHJvbmcgYuG7mSBk4buvIGxp4buHdSBuw6B5IGPDsyBgciBob25uaGFuWzFdYCBraMOhY2ggaMOgbmcgxJHDoyBr4bq/dCBow7RuIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX2hvbm5oYW4kTGFiZWxbMV1gIHbDoCBgciBob25uaGFuWzJdYCBraMOhY2ggaMOgbmcgxJHhu5ljIHRow6JuIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX2hvbm5oYW4kTGFiZWxbMl1gLg0KDQojIyMgMy4gSG9tZW93bmVyDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCm5oYSA8LSB0YWJsZShkdCRIb21lb3duZXIpDQpuaGENCmBgYA0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBzdeG6pXQNCnRhYmxlKGR0JEhvbWVvd25lcikvc3VtKG5yb3coZHQpKQ0KYGBgDQoNCmBgYHtyfQ0KIyDEkMawYSB24buBIGThuqFuZyBkYXRhLmZyYW1lDQpkZl9uaGEgPC0gYXMuZGF0YS5mcmFtZShuaGEpDQpjb2xuYW1lcyhkZl9uaGEpIDwtIGMoIkhvbWVvd25lciIsICJDb3VudCIpDQoNCmRmX25oYSRQZXJjZW50IDwtIHJvdW5kKGRmX25oYSRDb3VudCAvIHN1bShkZl9uaGEkQ291bnQpICogMTAwLCAxKQ0KZGZfbmhhJExhYmVsIDwtIHBhc3RlMChkZl9uaGEkUGVyY2VudCwgIiUiKQ0KDQpnZ3Bsb3QoZGZfbmhhLCBhZXMoeCA9ICIiLCB5ID0gQ291bnQsIGZpbGwgPSBIb21lb3duZXIpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsNCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IExhYmVsKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgbGFicyh0aXRsZSA9ICJCaeG7g3UgxJHhu5MgMzogQmnhu4N1IMSR4buTIHRo4buDIGhp4buHbiB0w6xuaCB0cuG6oW5nIG5ow6Ag4bufIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKCJZIiA9ICJzdGVlbGJsdWUiLCAiTiIgPSAidG9tYXRvIiksDQogICAgbGFiZWxzID0gYygiWSIgPSAixJDDoyBjw7MgbmjDoCIsICJOIiA9ICJDaMawYSBjw7MgbmjDoCIpDQogICkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpKQ0KDQpgYGANCg0KVuG6rXkgdHJvbmcgYuG7mSBk4buvIGxp4buHdSBuw6B5IGPDsyBgciBuaGFbMV1gIGtow6FjaCBow6BuZyBjaMawYSBjw7MgbmjDoCBjaGnhur9tIHThu4kgbOG7hyBgciBkZl9uaGEkTGFiZWxbMV1gIHbDoCBgciBuaGFbMl1gIGtow6FjaCBow6BuZyDEkcOjIGPDsyBuaMOgIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX25oYSRMYWJlbFsyXWAuDQoNCg0KIyMjIDQuIEFubnVhbEluY29tZQ0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCnRodW5oYXAgPC0gdGFibGUoZHQkQW5udWFsSW5jb21lKQ0KdGh1bmhhcA0KYGBgDQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc3XhuqV0DQp0YWJsZShkdCRBbm51YWxJbmNvbWUpL3N1bShucm93KGR0KSkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBDaHV54buDbiBzYW5nIGThuqFuZyBkYXRhIGZyYW1lDQpkZl90aHVuaGFwIDwtIGFzLmRhdGEuZnJhbWUodGh1bmhhcCkNCmNvbG5hbWVzKGRmX3RodW5oYXApIDwtIGMoIkFubnVhbEluY29tZSIsICJDb3VudCIpDQoNCmRmX3RodW5oYXAkUGVyY2VudCA8LSByb3VuZChkZl90aHVuaGFwJENvdW50IC8gc3VtKGRmX3RodW5oYXAkQ291bnQpICogMTAwLCAxKQ0KZGZfdGh1bmhhcCRMYWJlbCA8LSBwYXN0ZTAoZGZfdGh1bmhhcCRQZXJjZW50LCAiJSIpDQoNCiMgVuG6vSBiaeG7g3UgxJHhu5MgY+G7mXQNCmdncGxvdChkZl90aHVuaGFwLCBhZXMoeCA9IEFubnVhbEluY29tZSwgeSA9IENvdW50LCBmaWxsID0gQW5udWFsSW5jb21lKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQ291bnQpLCB2anVzdCA9IC0wLjUpICsNCiAgbGFicyh0aXRsZSA9ICJCaeG7g3UgxJHhu5MgNDogQmnhu4N1IMSR4buTIHBow6JuIGLhu5EgbeG7qWMgdGh1IG5o4bqtcCIsDQogICAgICAgeCA9ICJLaG/huqNuZyB0aHUgbmjhuq1wIHRoZW8gbmjDs20iLA0KICAgICAgIHkgPSAiU+G7kSBuZ8aw4budaSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyDhuqhuIGNow7ogdGjDrWNoIG3DoHUgdsOsIMSRw6MgaGnhu4duIHRyw6puIHRy4bulYyB4DQoNCmBgYA0KDQpW4bqteSB0cm9uZyBi4buZIGThu68gbGnhu4d1IG7DoHkgY8OzOg0KDQotIGByIHRodW5oYXBbMV1gIGtow6FjaCBow6BuZyB0aHXhu5ljIG5ow7NtIHRodSBuaOG6rXAgJDEwSyAtICQzMEssIGNoaeG6v20gdOG7tyBs4buHIGByIGRmX3RodW5oYXAkTGFiZWxbMV1gLg0KLSBgciB0aHVuaGFwWzJdYCBraMOhY2ggaMOgbmcgdGh14buZYyBuaMOzbSB0aHUgbmjhuq1wICQxMTBLIC0gJDEzMEssIGNoaeG6v20gdOG7tyBs4buHIGByIGRmX3RodW5oYXAkTGFiZWxbMl1gLg0KLSBgciB0aHVuaGFwWzNdYCBraMOhY2ggaMOgbmcgdGh14buZYyBuaMOzbSB0aHUgbmjhuq1wICQxMjBLIC0gJDE1MEssIGNoaeG6v20gdOG7tyBs4buHIGByIGRmX3RodW5oYXAkTGFiZWxbM11gLg0KLSBgciB0aHVuaGFwWzRdYCBraMOhY2ggaMOgbmcgdGh14buZYyBuaMOzbSB0aHUgbmjhuq1wICQxNTBLICsgLCBjaGnhur9tIHThu7cgbOG7hyBgciBkZl90aHVuaGFwJExhYmVsWzRdYC4NCi0gYHIgdGh1bmhhcFs1XWAga2jDoWNoIGjDoG5nIHRodeG7mWMgbmjDs20gdGh1IG5o4bqtcCAkMzBLIC0gJDUwSywgY2hp4bq/bSB04bu3IGzhu4cgYHIgZGZfdGh1bmhhcCRMYWJlbFs1XWAuDQotIGByIHRodW5oYXBbNl1gIGtow6FjaCBow6BuZyB0aHXhu5ljIG5ow7NtIHRodSBuaOG6rXAgJDUwSyAtICQ3MEssIGNoaeG6v20gdOG7tyBs4buHIGByIGRmX3RodW5oYXAkTGFiZWxbNl1gLg0KLSBgciB0aHVuaGFwWzddYCBraMOhY2ggaMOgbmcgdGh14buZYyBuaMOzbSB0aHUgbmjhuq1wICQ3MEsgLSAkOTBLLCBjaGnhur9tIHThu7cgbOG7hyBgciBkZl90aHVuaGFwJExhYmVsWzddYC4NCi0gYHIgdGh1bmhhcFs4XWAga2jDoWNoIGjDoG5nIHRodeG7mWMgbmjDs20gdGh1IG5o4bqtcCAkOTBLIC0gJDExMEssIGNoaeG6v20gdOG7tyBs4buHIGByIGRmX3RodW5oYXAkTGFiZWxbOF1gLg0KDQojIyMgNS4gQ2l0eQ0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCnRoYW5ocGhvIDwtIHRhYmxlKGR0JENpdHkpDQp0aGFuaHBobw0KYGBgDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHN14bqldA0KdGFibGUoZHQkQ2l0eSkvc3VtKG5yb3coZHQpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIENodXnhu4NuIHNhbmcgZOG6oW5nIGRhdGEgZnJhbWUNCmRmX3RoYW5ocGhvIDwtIGFzLmRhdGEuZnJhbWUodGhhbmhwaG8pDQpjb2xuYW1lcyhkZl90aGFuaHBobykgPC0gYygiQ2l0eSIsICJDb3VudCIpDQoNCmRmX3RoYW5ocGhvJFBlcmNlbnQgPC0gcm91bmQoZGZfdGhhbmhwaG8kQ291bnQgLyBzdW0oZGZfdGhhbmhwaG8kQ291bnQpICogMTAwLCAxKQ0KZGZfdGhhbmhwaG8kTGFiZWwgPC0gcGFzdGUwKGRmX3RoYW5ocGhvJFBlcmNlbnQsICIlIikNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyBj4buZdA0KZ2dwbG90KGRmX3RoYW5ocGhvLCBhZXMoeCA9IENpdHksIHkgPSBDb3VudCwgZmlsbCA9IENpdHkpKSArDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudCksIHZqdXN0ID0gLTAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyA1OiBCaeG7g3UgxJHhu5MgcGjDom4gYuG7kSB0aGVvIHRow6BuaCBwaOG7kSIsDQogICAgICAgeCA9ICJUaMOgbmggcGjhu5EiLCANCiAgICAgICB5ID0gIlPhu5EgbmfGsOG7nWkiLA0KICAgICAgIGZpbGwgPSAiQ2l0eSIpICsgDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksICMg4bqobiBjaOG7ryBkxrDhu5tpIHRy4bulYyBYDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCg0KYGBgDQoNClRyb25nIGLhu5kgZOG7ryBsaeG7h3UgbsOgeSBjw7MgdOG7lW5nIGPhu5luZyAyMyB0aMOgbmggcGjhu5EuIFRyb25nIMSRw7M6DQoNCi0gVGjDoG5oIHBo4buRIGPDsyBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBuaGnhu4F1IG5o4bqldCBsw6AgYHIgZGZfdGhhbmhwaG8kQ2l0eVsxM11gIHbhu5tpIGByIHRoYW5ocGhvWzEzXWAgbmfGsOG7nWkgY2hp4bq/bSB04buJIGzhu4cgYHIgZGZfdGhhbmhwaG8kTGFiZWxbMTNdYCwNCg0KLSBUaeG6v3AgdGhlbyBsw6AgYHIgZGZfdGhhbmhwaG8kQ2l0eVsxOV1gIHbhu5tpIGByIGRmX3RoYW5ocGhvJExhYmVsWzE5XWAgLCBgciBkZl90aGFuaHBobyRDaXR5WzhdYCB2w6AgYHIgZGZfdGhhbmhwaG8kQ2l0eVsxN11gIHbhu5tpIHThu4kgbOG7hyBs4bqnbiBsxrDhu6N0IGzDoCBgciBkZl90aGFuaHBobyRMYWJlbFs4XWAgdsOgIGByIGRmX3RoYW5ocGhvJExhYmVsWzE3XWAuDQoNCkPDoWMgdGjDoG5oIHBo4buRIGPDsm4gbOG6oWkgY2hp4bq/bSB04bu3IGzhu4cgbmjhu48gaMahbiwgZGFvIMSR4buZbmcgdOG7qyByIGRmX3RoYW5ocGhvJExhYmVsWzRdIMSR4bq/biByIGRmX3RoYW5ocGhvJExhYmVsWzEwXS4NCg0KDQojIyMgNi4gU3RhdGVvclByb3ZpbmNlDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCnRpbmggPC0gdGFibGUoZHQkU3RhdGVvclByb3ZpbmNlKQ0KdGluaA0KYGBgIA0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHN14bqldA0KdGFibGUoZHQkU3RhdGVvclByb3ZpbmNlKS9zdW0obnJvdyhkdCkpDQpgYGANCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIENodXnhu4NuIHNhbmcgZOG6oW5nIGRhdGEgZnJhbWUNCmRmX3RpbmggPC0gYXMuZGF0YS5mcmFtZSh0aW5oKQ0KY29sbmFtZXMoZGZfdGluaCkgPC0gYygiU3RhdGVvclByb3ZpbmNlIiwgIkNvdW50IikNCg0KZGZfdGluaCRQZXJjZW50IDwtIHJvdW5kKGRmX3RpbmgkQ291bnQgLyBzdW0oZGZfdGluaCRDb3VudCkgKiAxMDAsIDEpDQpkZl90aW5oJExhYmVsIDwtIHBhc3RlMChkZl90aW5oJFBlcmNlbnQsICIlIikNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyBj4buZdA0KZ2dwbG90KGRmX3RpbmgsIGFlcyh4ID0gU3RhdGVvclByb3ZpbmNlLCB5ID0gQ291bnQsIGZpbGwgPSBTdGF0ZW9yUHJvdmluY2UpKSArDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudCksIHZqdXN0ID0gLTAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyA2OiBCaeG7g3UgxJHhu5MgcGjDom4gYuG7kSB0aGVvIHThu4luaCIsDQogICAgICAgeCA9ICJU4buJbmgiLCANCiAgICAgICB5ID0gIlPhu5EgbmfGsOG7nWkiLA0KICAgICAgIGZpbGwgPSAiU3RhdGVvclByb3ZpbmNlIikgKyANCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgIyDhuqhuIGNo4buvIGTGsOG7m2kgdHLhu6VjIFgNCmBgYA0KDQpUcm9uZyBi4buZIGThu68gbGnhu4d1IG7DoHkgY8OzIHThu5VuZyBj4buZbmcgMTAgdOG7iW5oLiBUcm9uZyDEkcOzOg0KDQotIFThu4luaCBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbmhp4buBdSBuaOG6pXQgbMOgIGByIGRmX3RpbmgkU3RhdGVvclByb3ZpbmNlWzhdYCB24bubaSBgciB0aW5oWzhdYCBuZ8aw4budaSBjaGnhur9tIHThu4kgbOG7hyBgciBkZl90aW5oJExhYmVsWzhdYCwNCg0KLSBUaeG6v3AgdGhlbyBsw6AgYHIgZGZfdGluaCRTdGF0ZW9yUHJvdmluY2VbMl1gLCBgciBkZl90aW5oJFN0YXRlb3JQcm92aW5jZVs2XWAgdsOgIGByIGRmX3RpbmgkU3RhdGVvclByb3ZpbmNlWzEwXWAgduG7m2kgdOG7iSBs4buHIGzhuqduIGzGsOG7o3QgbMOgIGByIGRmX3RpbmgkTGFiZWxbMl1gLCBgciBkZl90aW5oJExhYmVsWzZdYCwgYHIgZGZfdGluaCRMYWJlbFsxMF1gLg0KDQotIEPDoWMgdOG7iW5oIGPDsm4gbOG6oWkgY2hp4bq/bSB04bu3IGzhu4cgbmjhu48gaMahbiwgZGFvIMSR4buZbmcgdOG7qyBgciBkZl90aW5oJExhYmVsWzVdYCDEkeG6v24gYHIgZGZfdGluaCRMYWJlbFszXWAuDQoNCg0KIyMjIDcuIENvdW50cnkNCg0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBz4buRDQpxdW9jZ2lhIDwtIHRhYmxlKGR0JENvdW50cnkpDQpxdW9jZ2lhDQpgYGANCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc3XhuqV0DQp0YWJsZShkdCRDb3VudHJ5KS9zdW0obnJvdyhkdCkpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQojIELGsOG7m2MgMTogxJDGsGEgduG7gSBkYXRhLmZyYW1lDQpkZl9xdW9jZ2lhIDwtIGFzLmRhdGEuZnJhbWUocXVvY2dpYSkNCmNvbG5hbWVzKGRmX3F1b2NnaWEpIDwtIGMoIkNvdW50cnkiLCAiQ291bnQiKQ0KDQojIELGsOG7m2MgMjogVMOtbmggcGjhuqduIHRyxINtIHbDoCBuaMOjbg0KZGZfcXVvY2dpYSRQZXJjZW50IDwtIHJvdW5kKGRmX3F1b2NnaWEkQ291bnQgLyBzdW0oZGZfcXVvY2dpYSRDb3VudCkgKiAxMDAsIDEpDQpkZl9xdW9jZ2lhJExhYmVsIDwtIHBhc3RlMChkZl9xdW9jZ2lhJFBlcmNlbnQsICIlIikNCg0KIyBCxrDhu5tjIDM6IFbhur0gYmnhu4N1IMSR4buTIHRyw7JuDQpnZ3Bsb3QoZGZfcXVvY2dpYSwgYWVzKHggPSAiIiwgeSA9IENvdW50LCBmaWxsID0gQ291bnRyeSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gTGFiZWwpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyA3OiBCaeG7g3UgxJHhu5MgdGjhu4MgaGnhu4duIG7GoWkgY8awIHRyw7ogdGhlbyBxdeG7kWMgZ2lhIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKCJiZWlnZSIsImRhcmtncmVlbiIsImRhcmtyZWQiKQ0KICApICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkNCg0KYGBgDQoNCg0KVuG6rXkgdHJvbmcgYuG7mSBk4buvIGxp4buHdSBuw6B5IGPDsyANCg0KLSBgciBxdW9jZ2lhWzFdYCBraMOhY2ggaMOgbmcgxJHhur9uIHThu6sgcXXhu5FjIGdpYSBgciBkZl9xdW9jZ2lhJENvdW50cnlbMV1gIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX3F1b2NnaWEkTGFiZWxbMV1gLCAgDQotIGByIHF1b2NnaWFbMl1gIGtow6FjaCBow6BuZyDEkeG6v24gdOG7qyBxdeG7kWMgZ2lhIGByIGRmX3F1b2NnaWEkQ291bnRyeVsyXWAgY2hp4bq/bSB04buJIGzhu4cgYHIgZGZfcXVvY2dpYSRMYWJlbFsyXWAsICANCi0gYHIgcXVvY2dpYVszXWAga2jDoWNoIGjDoG5nIMSR4bq/biB04burIHF14buRYyBnaWEgYHIgZGZfcXVvY2dpYSRDb3VudHJ5WzNdYCBjaGnhur9tIHThu4kgbOG7hyBgciBkZl9xdW9jZ2lhJExhYmVsWzNdYC4NCg0KDQojIyMgOC4gUHJvZHVjdEZhbWlseQ0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCmZhbWlseSA8LSB0YWJsZShkdCRQcm9kdWN0RmFtaWx5KQ0KZmFtaWx5DQpgYGANCg0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBzdeG6pXQNCnRhYmxlKGR0JFByb2R1Y3RGYW1pbHkpL3N1bShucm93KGR0KSkNCmBgYA0KDQpgYGB7cn0NCiMgVOG6oW8gYuG6o25nIHbDoCDEkcawYSB24buBIGThuqFuZyBkYXRhLmZyYW1lDQpkZl9mYW1pbHkgPC0gYXMuZGF0YS5mcmFtZShmYW1pbHkpDQpjb2xuYW1lcyhkZl9mYW1pbHkpIDwtIGMoIlByb2R1Y3RGYW1pbHkiLCAiQ291bnQiKQ0KDQojIFTDrW5oIHBo4bqnbiB0csSDbSB2w6AgbmjDo24NCmRmX2ZhbWlseSRQZXJjZW50IDwtIHJvdW5kKGRmX2ZhbWlseSRDb3VudCAvIHN1bShkZl9mYW1pbHkkQ291bnQpICogMTAwLCAxKQ0KZGZfZmFtaWx5JExhYmVsIDwtIHBhc3RlMChkZl9mYW1pbHkkUGVyY2VudCwgIiUiKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTIHRyw7JuDQpnZ3Bsb3QoZGZfZmFtaWx5LCBhZXMoeCA9ICIiLCB5ID0gQ291bnQsIGZpbGwgPSBQcm9kdWN0RmFtaWx5KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBMYWJlbCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGxhYnModGl0bGUgPSAiQmnhu4N1IMSR4buTIDg6IFBow6JuIGLhu5EgbmjDs20gc+G6o24gcGjhuqltIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpKQ0KDQpgYGANCg0KDQpW4bqteSB0cm9uZyBi4buZIGThu68gbGnhu4d1IG7DoHkgY8OzDQoNCi0gYHIgZmFtaWx5WzFdYCBraMOhY2ggaMOgbmcgbXVhIHPhuqNuIHBo4bqpbSB0aHXhu5ljIG5ow7NtIGByIGRmX2ZhbWlseSRGYW1pbHlTdGF0dXNbMV1gIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX2ZhbWlseSRMYWJlbFsxXWAsICANCi0gYHIgZmFtaWx5WzJdYCBraMOhY2ggaMOgbmcgbXVhIHPhuqNuIHBo4bqpbSB0aHXhu5ljIG5ow7NtIGByIGRmX2ZhbWlseSRGYW1pbHlTdGF0dXNbMl1gIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX2ZhbWlseSRMYWJlbFsyXWAsICANCi0gYHIgZmFtaWx5WzNdYCBraMOhY2ggaMOgbmcgbXVhIHPhuqNuIHBo4bqpbSB0aHXhu5ljIG5ow7NtIGByIGRmX2ZhbWlseSRGYW1pbHlTdGF0dXNbM11gIGNoaeG6v20gdOG7iSBs4buHIGByIGRmX2ZhbWlseSRMYWJlbFszXWAuDQoNCg0KIyMjIDkuIFByb2R1Y3REZXBhcnRtZW50DQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc+G7kQ0KZGVwYXJ0bWVudCA8LSB0YWJsZShkdCRQcm9kdWN0RGVwYXJ0bWVudCkNCmRlcGFydG1lbnQNCmBgYA0KDQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc3XhuqV0DQp0YWJsZShkdCRQcm9kdWN0RGVwYXJ0bWVudCkvc3VtKG5yb3coZHQpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIENodXnhu4NuIHNhbmcgZOG6oW5nIGRhdGEgZnJhbWUNCmRmX2RlcGFydG1lbnQgPC0gYXMuZGF0YS5mcmFtZShkZXBhcnRtZW50KQ0KY29sbmFtZXMoZGZfZGVwYXJ0bWVudCkgPC0gYygiUHJvZHVjdERlcGFydG1lbnQiLCAiQ291bnQiKQ0KDQpkZl9kZXBhcnRtZW50JFBlcmNlbnQgPC0gcm91bmQoZGZfZGVwYXJ0bWVudCRDb3VudCAvIHN1bShkZl9kZXBhcnRtZW50JENvdW50KSAqIDEwMCwgMSkNCmRmX2RlcGFydG1lbnQkTGFiZWwgPC0gcGFzdGUwKGRmX2RlcGFydG1lbnQkUGVyY2VudCwgIiUiKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTIGPhu5l0DQpnZ3Bsb3QoZGZfZGVwYXJ0bWVudCwgYWVzKHggPSBQcm9kdWN0RGVwYXJ0bWVudCwgeSA9IENvdW50LCBmaWxsID0gUHJvZHVjdERlcGFydG1lbnQpKSArDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudCksIHZqdXN0ID0gLTAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyA5OiBCaeG7g3UgxJHhu5MgcGjDom4gYuG7kSBi4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0iLA0KICAgICAgIHggPSAiQ8OhYyBi4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0iLCANCiAgICAgICB5ID0gIlPhu5EgbmfGsOG7nWkiLA0KICAgICAgIGZpbGwgPSAiUHJvZHVjdERlcGFydG1lbnQiKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAjIOG6qG4gbmjDo24gZMaw4bubaSB0cuG7pWMgWCBu4bq/dSBj4bqnbg0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iDQogICkNCg0KYGBgDQoNClRyb25nIGLhu5kgZOG7ryBsaeG7h3UgbsOgeSBjw7MgdOG7lW5nIGPhu5luZyAyMiBi4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0uIFRyb25nIMSRw7M6DQoNCvCflLYgTmjDs20gYuG7mSBwaOG6rW4gc+G6o24gcGjhuqltIGPDsyBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBuaGnhu4F1IG5o4bqldCBn4buTbToNCg0KYHIgZGZfZGVwYXJ0bWVudCRQcm9kdWN0RGVwYXJ0bWVudFsxOF1gIHbhu5tpIGByIGRlcGFydG1lbnRbMThdYCBuZ8aw4budaSBjaGnhur9tIHThu7cgbOG7hyBgciBkZl9kZXBhcnRtZW50JExhYmVsWzE4XWAsDQoNCmByIGRmX2RlcGFydG1lbnQkUHJvZHVjdERlcGFydG1lbnRbMjBdYCB24bubaSBgciBkZXBhcnRtZW50WzIwXWAgbmfGsOG7nWkgY2hp4bq/bSB04bu3IGzhu4cgYHIgZGZfZGVwYXJ0bWVudCRMYWJlbFsyMF1gLg0KDQrwn5+gIE5ow7NtIGLhu5kgcGjhuq1uIHPhuqNuIHBo4bqpbSBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgdHJ1bmcgYsOsbmggZ+G7k206DQoNCmByIGRmX2RlcGFydG1lbnQkUHJvZHVjdERlcGFydG1lbnRbMTVdYCxgciBkZl9kZXBhcnRtZW50JFByb2R1Y3REZXBhcnRtZW50WzEzXWAsIGByIGRmX2RlcGFydG1lbnQkUHJvZHVjdERlcGFydG1lbnRbM11gLCBgciBkZl9kZXBhcnRtZW50JFByb2R1Y3REZXBhcnRtZW50WzZdYCwgYHIgZGZfZGVwYXJ0bWVudCRQcm9kdWN0RGVwYXJ0bWVudFsxMF1gLCBgciBkZl9kZXBhcnRtZW50JFByb2R1Y3REZXBhcnRtZW50WzE0XWAgduG7m2kgdOG7iSBs4buHIGdpYW8gxJHhu5luZyB04burICBgciBkZl9kZXBhcnRtZW50JExhYmVsWzE0XWAgxJHhur9uIGByIGRmX2RlcGFydG1lbnQkTGFiZWxbMTVdYC4NCg0K8J+foiBOaMOzbSBi4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0gY8OzIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIMOtdCBuaOG6pXQgZ+G7k206DQoNCkPDoWMgYuG7mSBwaOG6rW4gc+G6o24gcGjhuqltIGPDsm4gbOG6oWkgduG7m2kgc+G7kSBraMOhY2ggaMOgbmcgcuG6pXQgw610IGdpYW8gxJHhu5luZyDhu58gbeG7qWMgYHIgZGZfZGVwYXJ0bWVudCRMYWJlbFs4XWAgxJHhur9uIGByIGRmX2RlcGFydG1lbnQkTGFiZWxbNF1gLg0KDQoNCiMjIyAxMC4gUHJvZHVjdENhdGVnb3J5DQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc+G7kQ0KY2F0ZWdvcnkgPC0gdGFibGUoZHQkUHJvZHVjdENhdGVnb3J5KQ0KY2F0ZWdvcnkNCmBgYA0KYGBge3J9DQojIEzhuq1wIGLhuqNuZyB04bqnbiBzdeG6pXQNCnRhYmxlKGR0JFByb2R1Y3RDYXRlZ29yeSkvc3VtKG5yb3coZHQpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIMSQxrBhIGThu68gbGnhu4d1IHbhu4EgZOG6oW5nIGRhdGEgZnJhbWUNCmRmX2NhdGVnb3J5IDwtIGFzLmRhdGEuZnJhbWUoY2F0ZWdvcnkpDQpjb2xuYW1lcyhkZl9jYXRlZ29yeSkgPC0gYygiUHJvZHVjdENhdGVnb3J5IiwgIkNvdW50IikNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyB24bubaSBuaGnhu4F1IG3DoHUNCmdncGxvdChkZl9jYXRlZ29yeSwgYWVzKHggPSByZW9yZGVyKFByb2R1Y3RDYXRlZ29yeSwgQ291bnQpLCB5ID0gQ291bnQsIGZpbGwgPSBQcm9kdWN0Q2F0ZWdvcnkpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIkJp4buDdSDEkeG7kyAxMDogVOG6p24gc3XhuqV0IGdpYW8gZOG7i2NoIHRoZW8gUHJvZHVjdCBDYXRlZ29yeSIsDQogICAgICAgeCA9ICJMb+G6oWkgc+G6o24gcGjhuqltIiwNCiAgICAgICB5ID0gIlPhu5EgZ2lhbyBk4buLY2giKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICAjIOG6qG4gY2jDuiB0aMOtY2ggbcOgdSBu4bq/dSBraMO0bmcgY+G6p24NCiAgKQ0KDQpgYGANCg0KDQoNCg0KDQoNCiMjIFBo4bqnbiAzOiDGr+G7m2MgbMaw4bujbmcgS2hv4bqjbmcgdsOgIEtp4buDbSDEkeG7i25oIEdp4bqjIHRodXnhur90IGNobyBU4bu3IGzhu4cgKE3hu5l0IGJp4bq/bikNCg0KIyMjIDMuMS4gR2VuZGVyIChGZW1hbGUpDQoNCg0KS2nhu4NtIMSR4buLbmggZ2nhuqMgdGh1eeG6v3Q6DQpIMDogdOG7tyBs4buHIEZlbWFsZSA9IDAuNQ0KDQpIMTogdOG7tyBs4buHIEZlbWFsZSDiiaAgMC41DQoNCmBgYHtyfQ0KDQojIEzhuqV5IHPhu5EgbMaw4bujbmcgRmVtYWxlIHbDoCB04buVbmcgc+G7kSBxdWFuIHPDoXQNCmNvdW50X2ZlbWFsZSA8LSBnaW9pdGluaFsiRiJdDQpuIDwtIHN1bShnaW9pdGluaCkNCg0KIyAxLiDGr+G7m2MgbMaw4bujbmcga2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIEZlbWFsZQ0KcHJvcC50ZXN0X2ZlbWFsZSA8LSBwcm9wLnRlc3QoY291bnRfZmVtYWxlLCBuLCBjb25mLmxldmVsID0gMC45NSkNCg0KIyBL4bq/dCBxdeG6oyBraG/huqNuZyB0aW4gY+G6rXkNCmNhdCgiS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIEZlbWFsZSBsw6A6ICIsDQogICAgcm91bmQocHJvcC50ZXN0X2ZlbWFsZSRjb25mLmludFsxXSwzKSwgIiDEkeG6v24gIiwgcm91bmQocHJvcC50ZXN0X2ZlbWFsZSRjb25mLmludFsyXSwzKSwgIlxuIikNCg0KIyAyLiBLaeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dCBIMDogcCA9IDAuNQ0KIyBwcm9wLnRlc3QgY8WpbmcgdHLhuqMgbHXDtG4gcC12YWx1ZQ0KY2F0KCJHacOhIHRy4buLIHAgY+G7p2Ega2nhu4NtIMSR4buLbmggbMOgOiAiLCBwcm9wLnRlc3RfZmVtYWxlJHAudmFsdWUsICJcbiIpDQoNCmlmIChwcm9wLnRlc3RfZmVtYWxlJHAudmFsdWUgPCAwLjA1KSB7DQogIGNhdCgiPT4gQsOhYyBi4buPIEgwLCB04bu3IGzhu4cgRmVtYWxlIGtow6FjIDAuNSB24bubaSBt4bupYyDDvSBuZ2jEqWEgMC4wNVxuIikNCn0gZWxzZSB7DQogIGNhdCgiPT4gS2jDtG5nIGLDoWMgYuG7jyBIMCwga2jDtG5nIGPDsyDEkeG7pyBi4bqxbmcgY2jhu6luZyDEkeG7gyBraOG6s25nIMSR4buLbmggdOG7tyBs4buHIEZlbWFsZSBraMOhYyAwLjVcbiIpDQp9DQoNCmBgYA0KIyMjIDMuMi4gSG9tZW93bmVyDQoNCmBgYHtyfQ0KDQojIEzhuqV5IHPhu5EgbmfGsOG7nWkgY8OzIG5ow6AgKCJZIikNCmNvdW50X3llcyA8LSBuaGFbIlkiXQ0Kbl9ob21lIDwtIHN1bShuaGEpDQoNCiMgVMOtbmgga2hv4bqjbmcgdGluIGPhuq15IDk1JQ0KcHJvcC50ZXN0X3llcyAgPC0gcHJvcC50ZXN0KGNvdW50X3llcywgbl9ob21lLCBjb25mLmxldmVsID0gMC45NSkNCg0KIyBL4bq/dCBxdeG6oyBraG/huqNuZyB0aW4gY+G6rXkNCmNhdCgiS2hv4bqjbmcgdGluIGPhuq15IDk1JSBjaG8gdOG7tyBs4buHIG5nxrDhu51pIGPDsyBuaMOgOlxuIikNCmNhdCgiVOG7qyIsIHJvdW5kKHByb3AudGVzdF95ZXMkY29uZi5pbnRbMV0sIDMpLCAixJHhur9uIiwgcm91bmQocHJvcC50ZXN0X3llcyRjb25mLmludFsyXSwgMyksICJcbiIpDQpjYXQoIlThu7cgbOG7hyDGsOG7m2MgbMaw4bujbmc6Iiwgcm91bmQocHJvcC50ZXN0X3llcyRlc3RpbWF0ZSwgMyksICJcblxuIikNCg0KIyBLaeG7g20gxJHhu4tuaCBI4oKAOiBwID0gMC41ICh04bu3IGzhu4cgbmfGsOG7nWkgY8OzIG5ow6AgYuG6sW5nIDUwJSkNCnRlc3RfaG9tZSA8LSBwcm9wLnRlc3QoY291bnRfeWVzLCBuX2hvbWUsIHAgPSAwLjUsIGNvbmYubGV2ZWwgPSAwLjk1KQ0KDQojIEvhur90IHF14bqjIGtp4buDbSDEkeG7i25oDQpjYXQoIkvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIGdp4bqjIHRodXnhur90IEgwOiB04bu3IGzhu4cgbmfGsOG7nWkgY8OzIG5ow6AgPSAwLjVcbiIpDQpjYXQoIkdpw6EgdHLhu4sgcC12YWx1ZToiLCByb3VuZCh0ZXN0X2hvbWUkcC52YWx1ZSwgNCksICJcbiIpDQoNCmlmICh0ZXN0X2hvbWUkcC52YWx1ZSA8IDAuMDUpIHsNCiAgY2F0KCI9PiBCw6FjIGLhu48gSDA6IFThu7cgbOG7hyBuZ8aw4budaSBjw7MgbmjDoCBraMOhYyAwLjUgKGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6og4bufIG3hu6ljIDUlKVxuIikNCn0gZWxzZSB7DQogIGNhdCgiPT4gS2jDtG5nIGLDoWMgYuG7jyBIMDogS2jDtG5nIGPDsyDEkeG7pyBi4bqxbmcgY2jhu6luZyDEkeG7gyBr4bq/dCBsdeG6rW4gdOG7tyBs4buHIG5nxrDhu51pIGPDsyBuaMOgIGtow6FjIDAuNVxuIikNCn0NCg0KYGBgDQoNCg0KIyMjIDMuMy4gTWFyaXRhbFN0YXR1cw0KDQoNCmBgYHtyfQ0KY291bnRfbWFycmllZCA8LSBob25uaGFuWyJNIl0NCm5fbWFyaXRhbCA8LSBzdW0oaG9ubmhhbikNCg0KDQpwcm9wLnRlc3RfbWFycmllZCA8LSBwcm9wLnRlc3QoY291bnRfbWFycmllZCwgbl9tYXJpdGFsLCBjb25mLmxldmVsID0gMC45NSkNCg0KY2F0KCJLaG/huqNuZyB0aW4gY+G6rXkgOTUlIGNobyB04bu3IGzhu4cgbmfGsOG7nWkgxJHDoyBr4bq/dCBow7RuOlxuIikNCmNhdCgiVOG7qyIsIHJvdW5kKHByb3AudGVzdF9tYXJyaWVkJGNvbmYuaW50WzFdLCAzKSwgIsSR4bq/biIsIHJvdW5kKHByb3AudGVzdF9tYXJyaWVkJGNvbmYuaW50WzJdLCAzKSwgIlxuIikNCmNhdCgiVOG7tyBs4buHIMaw4bubYyBsxrDhu6NuZzoiLCByb3VuZChwcm9wLnRlc3RfbWFycmllZCRlc3RpbWF0ZSwgMyksICJcblxuIikNCg0KdGVzdF9tYXJyaWVkIDwtIHByb3AudGVzdChjb3VudF9tYXJyaWVkLCBuX21hcml0YWwsIHAgPSAwLjUsIGNvbmYubGV2ZWwgPSAwLjk1KQ0KDQpjYXQoIkvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIA0KZ2nhuqMgdGh1eeG6v3QgSDA6IHThu7cgbOG7hyBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4gPSAwLjVcbiIsDQogICAgImdp4bqjIHRodXnhur90IEgxOiB04bu3IGzhu4cgbmfGsOG7nWkgxJHDoyBr4bq/dCBow7RuIOKJoCAwLjVcbiIpDQpjYXQoIkdpw6EgdHLhu4sgcC12YWx1ZToiLCByb3VuZCh0ZXN0X21hcnJpZWQkcC52YWx1ZSwgNCksICJcbiIpDQoNCmlmICh0ZXN0X21hcnJpZWQkcC52YWx1ZSA8IDAuMDUpIHsNCiAgY2F0KCI9PiBCw6FjIGLhu48gSDA6IFThu7cgbOG7hyBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4ga2jDoWMgMC41IChjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIOG7nyBt4bupYyA1JSlcbiIpDQp9IGVsc2Ugew0KICBjYXQoIj0+IEtow7RuZyBiw6FjIGLhu48gSDA6IEtow7RuZyBjw7MgxJHhu6cgYuG6sW5nIGNo4bupbmcgxJHhu4Mga+G6v3QgbHXhuq1uIHThu7cgbOG7hyBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4ga2jDoWMgMC41XG4iKQ0KfQ0KDQoNCmBgYA0KDQojIyBQaOG6p24gNDogUGjDom4gdMOtY2ggbeG7kWkgcXVhbiBo4buHIGdp4buvYSBoYWkgYmnhur9uIMSQ4buLbmggdMOtbmggDQoNCiMjIyA0LjEuIEdlbmRlciB2w6AgSG9tZW93bmVyDQoNCiMjIyMgNC4xLjEuIFRo4buRbmcga8OqIG3DtCB04bqjIA0KDQpgYGB7cn0NCiMgQuG6o25nIHThuqduIHN14bqldCBjaMOpbyBnaeG7r2EgR2VuZGVyIHbDoCBIb21lb3duZXINCnRhYmxlX2dlbmRlcl9ob21lIDwtIHRhYmxlKGR0JEdlbmRlciwgZHQkSG9tZW93bmVyKQ0KdGFibGVfZ2VuZGVyX2hvbWUNCmBgYA0KYGBge3J9DQojIFThu7cgbOG7hyBwaOG6p24gdHLEg20gdGhlbyBow6BuZyAodHJvbmcgdOG7q25nIGdp4bubaSB0w61uaCwgY8OzIG5ow6AgY2hp4bq/bSBiYW8gbmhpw6p1ICUpDQpwcm9wX2dlbmRlcl9ob21lX3JvdyA8LSBwcm9wLnRhYmxlKHRhYmxlX2dlbmRlcl9ob21lLCAxKSAqIDEwMA0Kcm91bmQocHJvcF9nZW5kZXJfaG9tZV9yb3csIDIpDQoNCmBgYA0KYGBge3J9DQpkaWZmX2NvdW50IDwtIHRhYmxlX2dlbmRlcl9ob21lWyJGIiwgIlkiXSAtIHRhYmxlX2dlbmRlcl9ob21lWyJNIiwgIlkiXQ0KZGlmZl9ub3Rfb3duZXJfY291bnQgPC10YWJsZV9nZW5kZXJfaG9tZVsiRiIsICJOIl0tIHRhYmxlX2dlbmRlcl9ob21lWyJNIiwgIk4iXQ0KDQpjYXQoIk7hu68gc+G7nyBo4buvdSBuaMOgIG5oaeG7gXUgaMahbiBuYW06IiwgZGlmZl9jb3VudCwgIm5nxrDhu51pIikNCmBgYA0KYGBge3J9DQpjYXQoIk7hu68ga2jDtG5nIHPhu58gaOG7r3UgbmjDoCBuaGnhu4F1IGjGoW4gbmFtOiIsIGRpZmZfbm90X293bmVyX2NvdW50LCAibmfGsOG7nWlcbiIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQ2h1eeG7g24gYuG6o25nIHNhbmcgZGF0YSBmcmFtZQ0KZGZfZ2VuZGVyX2hvbWUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZV9nZW5kZXJfaG9tZSkNCmNvbG5hbWVzKGRmX2dlbmRlcl9ob21lKSA8LSBjKCJHZW5kZXIiLCAiSG9tZW93bmVyIiwgIkNvdW50IikNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyBj4buZdCBuaMOzbQ0KZ2dwbG90KGRmX2dlbmRlcl9ob21lLCBhZXMoeCA9IEdlbmRlciwgeSA9IENvdW50LCBmaWxsID0gSG9tZW93bmVyKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGxhYnModGl0bGUgPSAiTeG7kWkgcXVhbiBo4buHIGdp4buvYSBHZW5kZXIgdsOgIEhvbWVvd25lciIsDQogICAgICAgeCA9ICJHZW5kZXIiLCB5ID0gIlPhu5EgbMaw4bujbmciLCBmaWxsID0gIkhvbWVvd25lciIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KTmjhuq1uIHjDqXQ6DQoNCi0gVHJvbmcgYuG7mSBk4buvIGxp4buHdSwgY8OzIDI4MjYgbuG7ryBraMO0bmcgc+G7nyBo4buvdSBuaMOgIHbDoCA0MzQ0IG7hu68gxJHDoyBjw7MgbmjDoCwgMjc4OSBuYW0ga2jDtG5nIHPhu58gaOG7r3UgbmjDoCB2w6AgNDEwMCBuYW0gxJHDoyBjw7MgbmjDoC4NCg0KLSBT4buRIG5nxrDhu51pIG7hu68gY8OzIG5ow6Agbmhp4buBdSBoxqFuIHPhu5EgbmfGsOG7nWkgbmFtIGPDsyBuaMOgIGLhurFuZyAyNDQgbmfGsOG7nWkuDQoNCi0gU+G7kSBuZ8aw4budaSBu4buvIGtow7RuZyBjw7MgbmjDoCBuaGnhu4F1IGjGoW4gc+G7kSBuZ8aw4budaSBuYW0ga2jDtG5nIGPDsyBuaMOgIGLhurFuZyAzNyBuZ8aw4budaS4NCg0KIyMjIyA0LjEuMi4gS2nhu4NtIMSR4buLbmggdGjhu5FuZyBrw6oNCg0KR2nhuqMgdGh1eeG6v3Qga2nhu4NtIMSR4buLbmg6DQoNCi0gSOKCgDogR2nhu5tpIHTDrW5oIHbDoCB2aeG7h2Mgc+G7nyBo4buvdSBuaMOgIGtow7RuZyDhuqNuaCBoxrDhu5tuZyDEkeG6v24gbmhhdS4NCg0KLSBI4oKBOiBHaeG7m2kgdMOtbmggdsOgIHZp4buHYyBz4bufIGjhu691IG5ow6AgY8OzIGxpw6puIHF1YW4gxJHhur9uIG5oYXUuDQoNCmBgYHtyfQ0KY2hpX2dlbmRlcl9ob21lIDwtIGNoaXNxLnRlc3QodGFibGVfZ2VuZGVyX2hvbWUpDQpjaGlfZ2VuZGVyX2hvbWUNCmNhdCgiR2nDoSB0cuG7iyBDaGktc3F1YXJlZDoiLCByb3VuZChjaGlfZ2VuZGVyX2hvbWUkc3RhdGlzdGljLCAyKSwgIlxuIikNCmNhdCgiQuG6rWMgdOG7sSBkbzoiLCBjaGlfZ2VuZGVyX2hvbWUkcGFyYW1ldGVyLCAiXG4iKQ0KY2F0KCJQLXZhbHVlOiIsIHJvdW5kKGNoaV9nZW5kZXJfaG9tZSRwLnZhbHVlLCA0KSwgIlxuIikNCg0KYGBgDQpgYGB7cn0NCmlmIChjaGlfZ2VuZGVyX2hvbWUkcC52YWx1ZSA8IDAuMDUpIHsNCiAgY2F0KCI9PiBCw6FjIGLhu48gSDA6IEPDsyBt4buRaSBsacOqbiBo4buHIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ogZ2nhu69hIEdlbmRlciB2w6AgSG9tZW93bmVyXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCI9PiBLaMO0bmcgYsOhYyBi4buPIEgwOiBLaMO0bmcgY8OzIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIHbhu4EgbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2EgR2VuZGVyIHbDoCBIb21lb3duZXJcbiIpDQp9DQoNCmBgYA0KDQojIyMgNC4yLiBNYXJpdGFsU3RhdHVzIHbDoCBIb21lb3duZXINCg0KIyMjIyA0LjIuMS4gVGjhu5FuZyBrw6ogbcO0IHThuqMNCmBgYHtyfQ0KIyBC4bqjbmcgdOG6p24gc3XhuqV0IGNow6lvIGdp4buvYSBNYXJpdGFsU3RhdHVzIHbDoCBIb21lb3duZXINCnRhYmxlX21hcml0YWxfaG9tZSA8LSB0YWJsZShkdCRNYXJpdGFsU3RhdHVzLCBkdCRIb21lb3duZXIpDQp0YWJsZV9tYXJpdGFsX2hvbWUNCmBgYA0KDQpgYGB7cn0NCiMgVOG7tyBs4buHIHBo4bqnbiB0csSDbSB0aGVvIGjDoG5nDQpwcm9wX21hcml0YWxfaG9tZV9yb3cgPC0gcHJvcC50YWJsZSh0YWJsZV9tYXJpdGFsX2hvbWUsIDEpICogMTAwDQpyb3VuZChwcm9wX21hcml0YWxfaG9tZV9yb3csIDIpDQpgYGANCg0KDQpgYGB7cn0NCiMgVMOtbmggY2jDqm5oIGzhu4djaCBz4buRIGzGsOG7o25nIHPhu58gaOG7r3UgbmjDoCB2w6Aga2jDtG5nIHPhu58gaOG7r3UgbmjDoA0KZGlmZl9vd25lcl9jb3VudCA8LSB0YWJsZV9tYXJpdGFsX2hvbWVbIk0iLCAiWSJdIC0gdGFibGVfbWFyaXRhbF9ob21lWyJTIiwgIlkiXQ0KZGlmZl9ub3Rfb3duZXJfY291bnQgPC0gdGFibGVfbWFyaXRhbF9ob21lWyJTIiwgIk4iXSAtIHRhYmxlX21hcml0YWxfaG9tZVsiTSIsICJOIl0NCg0KIyBJbiBr4bq/dCBxdeG6ow0KY2F0KCJOZ8aw4budaSDEkcOjIGvhur90IGjDtG4gc+G7nyBo4buvdSBuaMOgIG5oaeG7gXUgaMahbiBuZ8aw4budaSDEkeG7mWMgdGjDom46IiwgZGlmZl9vd25lcl9jb3VudCwgIm5nxrDhu51pXG4iKQ0KY2F0KCJOZ8aw4budaSDEkeG7mWMgdGjDom4ga2jDtG5nIHPhu58gaOG7r3UgbmjDoCBuaGnhu4F1IGjGoW4gbmfGsOG7nWkgxJHDoyBr4bq/dCBow7RuOiIsIGRpZmZfbm90X293bmVyX2NvdW50LCAibmfGsOG7nWlcbiIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQ2h1eeG7g24gc2FuZyBkYXRhIGZyYW1lDQpkZl9tYXJpdGFsX2hvbWUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZV9tYXJpdGFsX2hvbWUpDQpjb2xuYW1lcyhkZl9tYXJpdGFsX2hvbWUpIDwtIGMoIk1hcml0YWxTdGF0dXMiLCAiSG9tZW93bmVyIiwgIkNvdW50IikNCg0KIyBW4bq9IGJp4buDdSDEkeG7kw0KZ2dwbG90KGRmX21hcml0YWxfaG9tZSwgYWVzKHggPSBNYXJpdGFsU3RhdHVzLCB5ID0gQ291bnQsIGZpbGwgPSBIb21lb3duZXIpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgbGFicyh0aXRsZSA9ICJN4buRaSBxdWFuIGjhu4cgZ2nhu69hIE1hcml0YWxTdGF0dXMgdsOgIEhvbWVvd25lciIsDQogICAgICAgeCA9ICJUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLCB5ID0gIlPhu5EgbMaw4bujbmciLCBmaWxsID0gIkPDsyBuaMOgIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQpOaOG6rW4geMOpdDoNCg0KLSA3NC45NiUgbmfGsOG7nWkgxJHDoyBr4bq/dCBow7RuIHPhu58gaOG7r3UgbmjDoCwgdHJvbmcga2hpIGNo4buJIGPDsyA0NS44NCUgbmfGsOG7nWkgxJHhu5ljIHRow6JuIHPhu58gaOG7r3UgbmjDoCB24bubaSBz4buRIG5nxrDhu51pIGNow6puaCBs4buHY2ggbMOgIDE4NTAgbmfGsOG7nWkuDQoNCi0gTmfGsOG7o2MgbOG6oWksIDU0LjE2JSBuZ8aw4budaSDEkeG7mWMgdGjDom4ga2jDtG5nIHPhu58gaOG7r3UgbmjDoCwgdHJvbmcga2hpIHThu7cgbOG7hyBuw6B5IOG7nyBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4gY2jhu4kgbMOgIDI1LjA0JSwgY2jDqm5oIGzhu4djaCBuaGF1IDIxNzcgbmfGsOG7nWkuDQoNCi0gU+G7sSBjaMOqbmggbOG7h2NoIG7DoHkgY2hvIHRo4bqleSBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4gY8OzIHh1IGjGsOG7m25nIHPhu58gaOG7r3UgbmjDoCBuaGnhu4F1IGjGoW4gbmfGsOG7nWkgxJHhu5ljIHRow6JuLg0KDQojIyMjIDQuMi4yLiBLaeG7g20gxJHhu4tuaCB0aOG7kW5nIGvDqg0KDQpHaeG6oyB0aHV54bq/dCBraeG7g20gxJHhu4tuaDoNCg0KLSBI4oKAOiBWaeG7h2Mga+G6v3QgaMO0biB2w6Agdmnhu4djIHPhu58gaOG7r3UgbmjDoCBraMO0bmcgbGnDqm4gcXVhbiDEkeG6v24gbmhhdS4NCg0KLSBI4oKBOiBWaeG7h2Mga+G6v3QgaMO0biB2w6Agdmnhu4djIHPhu58gaOG7r3UgbmjDoCBjw7MgbGnDqm4gcXVhbiDEkeG6v24gbmhhdS4NCg0KDQpgYGB7cn0NCmNoaV9tYXJpdGFsX2hvbWUgPC0gY2hpc3EudGVzdCh0YWJsZV9tYXJpdGFsX2hvbWUpDQpjaGlfbWFyaXRhbF9ob21lDQoNCmBgYA0KDQoNCmBgYHtyfQ0KY2F0KCJHacOhIHRy4buLIENoaS1zcXVhcmVkOiIsIHJvdW5kKGNoaV9tYXJpdGFsX2hvbWUkc3RhdGlzdGljLCAyKSwgIlxuIikNCmNhdCgiQuG6rWMgdOG7sSBkbzoiLCBjaGlfbWFyaXRhbF9ob21lJHBhcmFtZXRlciwgIlxuIikNCmNhdCgiUC12YWx1ZToiLCByb3VuZChjaGlfbWFyaXRhbF9ob21lJHAudmFsdWUsIDQpLCAiXG4iKQ0KDQppZiAoY2hpX21hcml0YWxfaG9tZSRwLnZhbHVlIDwgMC4wNSkgew0KICBjYXQoIj0+IELDoWMgYuG7jyBI4oKAOiBDw7MgbeG7kWkgbGnDqm4gaOG7hyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIGdp4buvYSBNYXJpdGFsU3RhdHVzIHbDoCBIb21lb3duZXJcbiIpDQp9IGVsc2Ugew0KICBjYXQoIj0+IEtow7RuZyBiw6FjIGLhu48gSOKCgDogS2jDtG5nIGPDsyBi4bqxbmcgY2jhu6luZyB0aOG7kW5nIGvDqiB24buBIG3hu5FpIGxpw6puIGjhu4cgZ2nhu69hIE1hcml0YWxTdGF0dXMgdsOgIEhvbWVvd25lclxuIikNCn0NCg0KYGBgDQoNCg0KDQoNCiMjIyA0LjMuIEdlbmRlciB2w6AgTWFyaXRhbFN0YXR1cw0KDQojIyMjIDQuMy4xLiBUaOG7kW5nIGvDqiBtw7QgdOG6ow0KDQpgYGB7cn0NCiMgVOG6oW8gYuG6o25nIHThuqduIHN14bqldCBjaMOpbyBnaeG7r2EgR2VuZGVyIHbDoCBNYXJpdGFsU3RhdHVzDQp0YWJsZV9nZW5kZXJfbWFyaXRhbCA8LSB0YWJsZShkdCRHZW5kZXIsIGR0JE1hcml0YWxTdGF0dXMpDQp0YWJsZV9nZW5kZXJfbWFyaXRhbA0KDQpgYGANCg0KDQpgYGB7cn0NCiMgVGhlbyBow6BuZzogdHJvbmcgdOG7q25nIGdp4bubaSB0w61uaCwgdOG7iSBs4buHIHThu6tuZyB0w6xuaCB0cuG6oW5nIGjDtG4gbmjDom4NCnByb3BfZ2VuZGVyX21hcml0YWxfcm93IDwtIHByb3AudGFibGUodGFibGVfZ2VuZGVyX21hcml0YWwsIDEpICogMTAwDQpyb3VuZChwcm9wX2dlbmRlcl9tYXJpdGFsX3JvdywgMikNCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpkZl9nZW5kZXJfbWFyaXRhbCA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlX2dlbmRlcl9tYXJpdGFsKQ0KY29sbmFtZXMoZGZfZ2VuZGVyX21hcml0YWwpIDwtIGMoIkdlbmRlciIsICJNYXJpdGFsU3RhdHVzIiwgIkNvdW50IikNCg0KZ2dwbG90KGRmX2dlbmRlcl9tYXJpdGFsLCBhZXMoeCA9IEdlbmRlciwgeSA9IENvdW50LCBmaWxsID0gTWFyaXRhbFN0YXR1cykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSArICAjIGhv4bq3YyAiZG9kZ2UiIG7hur91IG114buRbiBz4buRIGzGsOG7o25nDQogIGxhYnModGl0bGUgPSAiVOG7tyBs4buHIE1hcml0YWxTdGF0dXMgdGhlbyBHZW5kZXIiLA0KICAgICAgIHggPSAiR2VuZGVyIiwgeSA9ICJU4bu3IGzhu4ciLCBmaWxsID0gIlTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjIyMgNC4zLjIuICBLaeG7g20gxJHhu4tuaCB0aOG7kW5nIGvDqg0KDQpHaeG6oyB0aHV54bq/dCBraeG7g20gxJHhu4tuaDoNCg0KLSBI4oKAOiBWaeG7h2Mga+G6v3QgaMO0biB2w6AgZ2nhu5tpIHTDrW5oIGtow7RuZyBsacOqbiBxdWFuIMSR4bq/biBuaGF1Lg0KDQotIEjigoE6IFZp4buHYyBr4bq/dCBow7RuIHbDoCBnaeG7m2kgdMOtbmggY8OzIGxpw6puIHF1YW4gxJHhur9uIG5oYXUuDQoNCg0KYGBge3J9DQpjaGlfZ2VuZGVyX21hcml0YWwgPC0gY2hpc3EudGVzdCh0YWJsZV9nZW5kZXJfbWFyaXRhbCkNCmNoaV9nZW5kZXJfbWFyaXRhbA0KDQpgYGANCg0KYGBge3J9DQpjYXQoIkdpw6EgdHLhu4sgQ2hpLXNxdWFyZWQ6Iiwgcm91bmQoY2hpX2dlbmRlcl9tYXJpdGFsJHN0YXRpc3RpYywgMiksICJcbiIpDQpjYXQoIkLhuq1jIHThu7EgZG86IiwgY2hpX2dlbmRlcl9tYXJpdGFsJHBhcmFtZXRlciwgIlxuIikNCmNhdCgiUC12YWx1ZToiLCByb3VuZChjaGlfZ2VuZGVyX21hcml0YWwkcC52YWx1ZSwgNCksICJcbiIpDQoNCmlmIChjaGlfZ2VuZGVyX21hcml0YWwkcC52YWx1ZSA8IDAuMDUpIHsNCiAgY2F0KCI9PiBCw6FjIGLhu48gSOKCgDogQ8OzIG3hu5FpIGxpw6puIGjhu4cgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiBnaeG7r2EgR2VuZGVyIHbDoCBNYXJpdGFsU3RhdHVzXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCI9PiBLaMO0bmcgYsOhYyBi4buPIEjigoA6IEtow7RuZyBjw7MgYuG6sW5nIGNo4bupbmcgdGjhu5FuZyBrw6ogduG7gSBt4buRaSBsacOqbiBo4buHIGdp4buvYSBHZW5kZXIgdsOgIE1hcml0YWxTdGF0dXNcbiIpDQp9DQoNCmBgYA0KIyMgUGjhuqduIDU6IFThu5VuZyBr4bq/dCB2w6AgVGjhuqNvIGx14bqtbg0KDQo8dWw+ICoqVuG7gSBnaeG7m2kgdMOtbmggdsOgIHPhu58gaOG7r3UgbmjDoCoqIDogPC91bD4NCg0KLSBU4bu3IGzhu4cgcGjhu6UgbuG7ryBz4bufIGjhu691IG5ow6AgY2FvIGjGoW4gbmFtIGdp4bubaSAoNjAuNTklIHNvIHbhu5tpIDU5LjUyJSkuDQoNCi0gU+G7kSBsxrDhu6NuZyBu4buvIHPhu58gaOG7r3UgbmjDoCBuaGnhu4F1IGjGoW4gbmFtIGdp4bubaSAyNDQgbmfGsOG7nWksIGNobyB0aOG6pXkgcGjhu6UgbuG7ryB0cm9uZyB04bqtcCBk4buvIGxp4buHdSBuw6B5IGPDsyB4dSBoxrDhu5tuZyDhu5VuIMSR4buLbmggaMahbiB24buBIGNo4buXIOG7ny4NCg0KPHVsPiAqKlbhu4EgdMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIHbDoCBz4bufIGjhu691IG5ow6A6Kio8L3VsPg0KDQotIFThu7cgbOG7hyBuZ8aw4budaSDEkcOjIGvhur90IGjDtG4gc+G7nyBo4buvdSBuaMOgIGNhbyB2xrDhu6N0IHRy4buZaSAoNzQuOTYlKSBzbyB24bubaSBuZ8aw4budaSDEkeG7mWMgdGjDom4gKDQ1Ljg0JSkuDQoNCi0gTmfGsOG7nWkgxJHDoyBr4bq/dCBow7RuIHPhu58gaOG7r3UgbmjDoCBuaGnhu4F1IGjGoW4gbmfGsOG7nWkgxJHhu5ljIHRow6JuIDE4NTAgbmfGsOG7nWkgY2hvIHRo4bqleSBz4buxIOG7lW4gxJHhu4tuaCB0w6BpIGNow61uaCBjYW8gaMahbiDhu58gbmjDs20gxJHDoyBs4bqtcCBnaWEgxJHDrG5oLg0KDQo8dWw+ICoqVuG7gSBt4buRaSBxdWFuIGjhu4cgZ2nhu69hIGdp4bubaSB0w61uaCB2w6AgdMOsbmggdHLhuqFuZyBow7RuIG5ow6JuOioqIDwvdWw+DQoNCi0gS2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCByw7UgcuG7h3QgduG7gSB04bu3IGzhu4cga+G6v3QgaMO0biBnaeG7r2EgbmFtIHbDoCBu4buvLg0KDQotIFBow6JuIHTDrWNoIHRo4buRbmcga8OqIGNobyB0aOG6pXkgbeG7kWkgcXVhbiBo4buHIGdp4buvYSBnaeG7m2kgdMOtbmggdsOgIHTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiBsw6AgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiAocC12YWx1ZSBy4bqldCBuaOG7jykuDQoNCj0+IMSQaeG7gXUgbsOgeSBn4bujaSDDvSBy4bqxbmcgZG9hbmggbmdoaeG7h3AgY8OzIHRo4buDIGPDom4gbmjhuq9jIGNoaeG6v24gbMaw4bujYyBwaMOibiBraMO6YyB0aOG7iyB0csaw4budbmcgdGhlbyBnaeG7m2kgdMOtbmggdsOgIHTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiDEkeG7gyB04buRaSDGsHUgaMOzYSB0aeG6v3AgY+G6rW4gc+G6o24gcGjhuqltLg0KDQoNCjx1bD4gKipI4bqhbiBjaOG6vyBj4bunYSBwaMOibiB0w61jaCAqKjwvdWw+DQoNCi0gQ2jhu4kgZMO5bmcgYmnhur9uIMSR4buLbmggdMOtbmggbsOqbiBraMO0bmcgdGjhu4MgeGVtIHjDqXQg4bqjbmggaMaw4bufbmcgY+G7p2EgY8OhYyB54bq/dSB04buRIMSR4buLbmggbMaw4bujbmcgbmjGsCB0aHUgbmjhuq1wIChBbm51YWxJbmNvbWUpIGhheSDEkeG7mSB0deG7lWkgKEFnZSkg4oCTIHbhu5FuIGPDsyB0aOG7gyDhuqNuaCBoxrDhu59uZyBt4bqhbmggxJHhur9uIGjDoG5oIHZpIG11YSBz4bqvbS4NCg0KLSBN4buZdCBz4buRIG5ow7NtIGPDsyBz4buRIGzGsOG7o25nIG5o4buPLCB2w60gZOG7pSBt4buZdCBz4buRIGjhuqFuZyBt4bulYyB0cm9uZyBQcm9kdWN0Q2F0ZWdvcnkgaG/hurdjIE1hcml0YWxTdGF0dXMsIGtoaeG6v24gcGjDom4gdMOtY2ggdGjhu5FuZyBrw6ogZOG7hSBi4buLIGzhu4djaC4NCg0KLSBLaMO0bmcgeMOpdCDEkeG6v24gaMOgbmggdmkgdGhlbyB0aOG7nWkgZ2lhbiAoc2Vhc29uYWxpdHksIHh1IGjGsOG7m25nKSwgaG/hurdjIGxv4bqhaSBz4bqjbiBwaOG6qW0gY+G7pSB0aOG7gyBtw6Aga2jDoWNoIGjDoG5nIHF1YW4gdMOibS4NCg0KPHVsPiAqKsSQ4buBIHh14bqldCBjaG8gZG9hbmggbmdoaeG7h3AqKiA8L3VsPg0KDQoNCi0gTmjhuq9tIMSR4bq/biBwaOG7pSBu4buvIMSRw6Mga+G6v3QgaMO0biBuaMawIG3hu5l0IG5ow7NtIGtow6FjaCBow6BuZyB0aeG7gW0gbsSDbmcgY2FvIOKAkyDhu5VuIMSR4buLbmggduG7gSB0w6BpIGNow61uaCB2w6AgY8OzIGto4bqjIG7Eg25nIHPhu58gaOG7r3UgbmjDoCBjYW8g4oaSIGPDsyB0aOG7gyBtdWEgY8OhYyBz4bqjbiBwaOG6qW0gY2FvIGPhuqVwLCDEkeG7i25oIGPGsC4NCg0KLSBOZ8aw4budaSDEkeG7mWMgdGjDom4gY8OzIHRo4buDIHBow7kgaOG7o3AgduG7m2kgY8OhYyBjaGnhur9uIGzGsOG7o2MgdGnhur9wIHRo4buLIGxpbmggaG/huqF0IGjGoW4sIHThuq1wIHRydW5nIHbDoG8gdHLhuqNpIG5naGnhu4dtIGhv4bq3YyBk4buLY2ggduG7pSB0aHXDqi4NCg0KLSBDw6FjIHPhuqNuIHBo4bqpbSBsacOqbiBxdWFuIMSR4bq/biBuaMOgIGPhu61hLCBnaWEgxJHDrG5oIG7Dqm4gxrB1IHRpw6puIGNobyBuaMOzbSDEkcOjIGvhur90IGjDtG4uDQoNCi0gVuG7m2kgbmFtIGdp4bubaSwgY+G6p24gbuG7mWkgZHVuZyB0aeG6v3AgdGjhu4sgY+G7pSB0aOG7gyBoxqFuIGRvIHThu7cgbOG7hyBz4bufIGjhu691IG5ow6AgdsOgIHPhu7Eg4buVbiDEkeG7i25oIGPDsyB4dSBoxrDhu5tuZyB0aOG6pXAgaMahbi4NCg0KDQpgYGB7cn0NCiMgR8OhbiBiaeG6v24gc+G7kSBsxrDhu6NuZyBuZ8aw4budaSB04burIENhbmFkYSB2w6AgTWV4aWNvDQpjYW5hZGFfY291bnQgPC0gcXVvY2dpYVsiQ2FuYWRhIl0NCm1leGljb19jb3VudCA8LSBxdW9jZ2lhWyJNZXhpY28iXQ0KDQojIFThu5VuZyBz4buRIG5nxrDhu51pIHThu6sgbeG7l2kgcXXhu5FjIGdpYSAo4bufIMSRw6J5IGPFqW5nIGzDoCBz4buRIGzGsOG7o25nIHThu6tuZyBuaMOzbSkNCm5fY2FuYWRhIDwtIGNhbmFkYV9jb3VudA0Kbl9tZXhpY28gPC0gbWV4aWNvX2NvdW50DQoNCiMgS2nhu4NtIMSR4buLbmggcHJvcC50ZXN0IHbhu5tpIGJp4bq/bg0KcHJvcC50ZXN0KHggPSBjKGNhbmFkYV9jb3VudCwgbWV4aWNvX2NvdW50KSwNCiAgICAgICAgICBuID0gYyhuX2NhbmFkYSwgbl9tZXhpY28pLA0KICAgICAgICAgIGFsdGVybmF0aXZlID0gImxlc3MiKQ0KYGBgDQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQoNCiMjIFBo4bqnbiA2OiBOSEnhu4ZNIFbhu6QgVFXhuqZOIDMNCg0KDQojIyMgNi4xLiBC4bqjbmcgbmfhuqt1IG5oacOqbiANCg0KQuG6o25nIG5n4bqrdSBuaGnDqm4gKHJhbmRvbSB0YWJsZSkgbMOgIG3hu5l0IGLhuqNuZyBob+G6t2MgbWEgdHLhuq1uIHRyb25nIMSRw7MgbeG7l2kgw7QgY2jhu6lhIG3hu5l0IGdpw6EgdHLhu4sgxJHGsOG7o2Mgc2luaCByYSB04burIG3hu5l0IGJp4bq/biBuZ+G6q3Ugbmhpw6puIHRoZW8gbeG7mXQgcGjDom4gcGjhu5FpIHjDoWMgxJHhu4tuaC4gQuG6o25nIG7DoHkgY8OzIHRo4buDIMSRxrDhu6NjIGTDuW5nIMSR4buDIG3DtCBwaOG7j25nLCBtw7QgaMOsbmggaMOzYSBjw6FjIGhp4buHbiB0xrDhu6NuZyBuZ+G6q3Ugbmhpw6puIHRyb25nIHThu7Egbmhpw6puLCB4w6MgaOG7mWkgaG/hurdjIGvhu7kgdGh14bqtdC4NCg0KLSBQaMOibiBwaOG7kWkgTXVsdGlub21pYWwgbeG7nyBy4buZbmcgY+G7p2EgcGjDom4gcGjhu5FpIG5o4buLIHRo4bupYyAoQmlub21pYWwpLCBkw7luZyDEkeG7gyBtw7QgaMOsbmggaMOzYSB4w6FjIHN14bqldCBj4bunYSBjw6FjIGvhur90IHF14bqjIHLhu51pIHLhuqFjIHThu6sgbeG7mXQgdGjhu60gbmdoaeG7h20gbOG6t3AgxJFpIGzhurdwIGzhuqFpLg0KDQotIFBow6JuIHBo4buRaSBQb2lzc29uIMSRxrDhu6NjIGTDuW5nIMSR4buDIG3DtCB04bqjIHPhu5EgbOG6p24geOG6o3kgcmEgY+G7p2EgbeG7mXQgc+G7sSBraeG7h24gdHJvbmcgbeG7mXQga2hv4bqjbmcgdGjhu51pIGdpYW4gaG/hurdjIGtow7RuZyBnaWFuIG5o4bqldCDEkeG7i25oIGtoaSBjw6FjIHPhu7Ega2nhu4duIHjhuqN5IHJhIMSR4buZYyBs4bqtcCB24bubaSBuaGF1Lg0KDQpC4bqjbmcgbmfhuqt1IG5oacOqbiAoY29udGluZ2VuY3kgdGFibGUpIGzDoCBjw7RuZyBj4bulIHRo4buRbmcga8OqIGTDuW5nIMSR4buDIHRyw6xuaCBiw6B5IHThuqduIHPhu5EgY+G7p2EgY8OhYyBiaeG6v24gcGjDom4gbG/huqFpLiBUcm9uZyB0csaw4budbmcgaOG7o3AgxJHGoW4gZ2nhuqNuIG5o4bqldCDigJMgYuG6o25nIDJ4MiDigJMgYuG6o25nIGPDsyBk4bqhbmc6DQoNCmBgYHtyfQ0KY2F0KCINCiAgICAgICAgICAgICAgQmnhur9uIEIgPSBZZXMgICAgQmnhur9uIEIgPSBObw0KQmnhur9uIEEgPSBZZXMgICAgICAgYSAgICAgICAgICAgICAgYg0KQmnhur9uIEEgPSBObyAgICAgICAgYyAgICAgICAgICAgICAgZA0KIikNCmBgYA0KDQojIyMjIDYuMS4xLiBQaMOibiBwaOG7kWkgUG9pc3Nvbg0KDQpQaMOibiBwaOG7kWkgUG9pc3NvbiBsw6AgbeG7mXQgcGjDom4gcGjhu5FpIHjDoWMgc3XhuqV0IHLhu51pIHLhuqFjIG3DtCB04bqjIHPhu5EgbOG6p24geOG6o3kgcmEgY+G7p2EgbeG7mXQgc+G7sSBraeG7h24gdHJvbmcgbeG7mXQga2hv4bqjbmcgdGjhu51pIGdpYW4gaG/hurdjIGtow7RuZyBnaWFuIG5o4bqldCDEkeG7i25oLCBraGkgY8OhYyBz4buxIGtp4buHbiB44bqjeSByYSDEkeG7mWMgbOG6rXAgdsOgIHbhu5tpIHThu5FjIMSR4buZIHjhuqN5IHJhIHRydW5nIGLDrG5oIGtow7RuZyDEkeG7lWkuDQokJA0KWF97aWp9IFxzaW0gXHRleHR7UG9pc3Nvbn0oXGxhbWJkYV97aWp9KQ0KJCQNClRyb25nIMSRw7M6IA0KDQpYOiBz4buRIHPhu7Ega2nhu4duIHjhuqN5IHJhIHRyb25nIGtob+G6o25nIHRo4budaSBnaWFuIGhv4bq3YyBraMO0bmcgZ2lhbiDEkcOjIHjDoWMgxJHhu4tuaA0KDQokXGxhbWJkYSA+IDAkOiBz4buRIGzhuqduIHjhuqN5IHJhIHRydW5nIGLDrG5oIHRyb25nIDEgxJHGoW4gduG7iyB0aOG7nWkgZ2lhbiBob+G6t2Mga2jDtG5nIGdpYW4NCg0KKipIw6BtIHjDoWMgc3XhuqV0IChQTUYpOioqDQoNCiQkDQpQKFggPSBrKSA9IFxmcmFje2Veey1cbGFtYmRhfSBcbGFtYmRhXmt9e2shfSwgXHF1YWQgXHRleHR7duG7m2kgfSBrID0gMCwgMSwgMiwgXGRvdHMNCiQkDQoNCiMjIyMgNi4xLjIuIFBow6JuIHBo4buRaSBCaW5vbWlhbA0KUGjDom4gcGjhu5FpIMSRYSB0aOG7qWMgbeG7nyBy4buZbmcgY+G7p2EgcGjDom4gcGjhu5FpIG5o4buLIHRo4bupYyAoQmlub21pYWwpIGNobyB0csaw4budbmcgaOG7o3AgY8OzIG5oaeG7gXUgaMahbiAyIGvhur90IHF14bqjIGPDsyB0aOG7gyB44bqjeSByYSB0cm9uZyBt4buXaSB0aOG7rSBuZ2hp4buHbSDEkeG7mWMgbOG6rXAuDQoNCioqSMOgbSB4w6FjIHN14bqldCoqDQoNCiQkDQpQKFhfMSA9IHhfMSwgXGRvdHMsIFhfayA9IHhfaykgPSBcZnJhY3tuIX17eF8xISB4XzIhIFxjZG90cyB4X2shfSBcY2RvdCBwXzFee3hfMX0gcF8yXnt4XzJ9IFxjZG90cyBwX2tee3hfa30NCiQkDQoNClRyb25nIMSRw7M6DQoNCiRYX2kgXHRleHR7IGzDoCBz4buRIGzhuqduIGvhur90IHF14bqjIHRo4bupIH0gaSBcdGV4dHsgeHXhuqV0IGhp4buHbiB0cm9uZyB9IG4gXHRleHR7IHRo4butfSQNCg0KJHhfaSBcZ2UgMCBccXVhZCBcdGV4dHt2w6B9IFxxdWFkIFxzdW1fe2k9MX1ee2t9IHhfaSA9IG4kDQoNCg0KDQojIyMgNi4yLiBU4bu3IGzhu4cgdHJvbmcgYuG6o25nIG5n4bqrdSBuaGnDqm4gMngyOg0KDQoNCiMjIyMgNi4yLjEuIEhp4buHdSBoYWkgdOG7tyBs4buHIChSaXNrIERpZmZlcmVuY2UgLSBSRCkNCg0KJCQNClJEID0gXGhhdHtwfV8xIC0gXGhhdHtwfV8yID0gXGZyYWN7YSArIGJ9e2F9IC0gXGZyYWN7YyArIGR9e2N9DQokJA0KVHJvbmcgxJHDszoNCg0KLSAkXGZyYWN7YX17YSArIGJ9IFx0ZXh0eyBsw6AgdOG7tyBs4buHIHjhuqN5IHJhIHPhu7Ega2nhu4duIHRyb25nIG5ow7NtIEEgPSBZZXMufSQNCg0KLSAkXGZyYWN7Y317YyArIGR9IFx0ZXh0eyBsw6AgdOG7tyBs4buHIHjhuqN5IHJhIHPhu7Ega2nhu4duIHRyb25nIG5ow7NtIEEgPSBOby59JA0KDQoNCiMjIyMgNi4yLjIuIFThu7cgc+G7kSBuZ3V5IGPGoSAoUmVsYXRpdmUgUmlzayAtIFJSKQ0KDQokJA0KUlIgPSBcZnJhY3tcaGF0e3B9XzJ9e1xoYXR7cH1fMX0gPSBcZnJhY3tcZnJhY3tjfXtjICsgZH19e1xmcmFje2F9e2EgKyBifX0NCiQkDQoNCioqw50gbmdoxKlhKioNCg0KVOG7tyBz4buRIG5ndXkgY8ahIMSRbyBsxrDhu51uZyBt4bupYyDEkeG7mSB0xINuZyAoaG/hurdjIGdp4bqjbSkgbmd1eSBjxqEgeOG6o3kgcmEgc+G7sSBraeG7h24g4bufIG5ow7NtIEEgPSBZZXMgc28gduG7m2kgbmjDs20gQSA9IE5vLiBUaOG7gyBoaeG7h24gbeG7qWMgdMSDbmcgaG/hurdjIGdp4bqjbSB0xrDGoW5nIMSR4buRaSBj4bunYSBuZ3V5IGPGoSB44bqjeSByYSBz4buxIGtp4buHbiB0cm9uZyBt4buZdCBuaMOzbSBzbyB24bubaSBuaMOzbSBjw7JuIGzhuqFpLg0KDQotIE7hur91IFJSPTE6IEtow7RuZyBjw7Mgc+G7sSBraMOhYyBiaeG7h3Qgbmd1eSBjxqEuDQoNCi0gTuG6v3UgUlI+MTogTmd1eSBjxqEg4bufIG5ow7NtIEEgPSBZZXMgY2FvIGjGoW4gbmjDs20gQSA9IE5vLg0KDQotIE7hur91IFJSPDE6IE5ndXkgY8ahIOG7nyBuaMOzbSBBID0gWWVzIHRo4bqlcCBoxqFuIG5ow7NtIEEgPSBOby4NCg0KDQojIyMjIDYuMi4zLiBU4bu3IHPhu5EgY2jDqm5oIChPZGRzIFJhdGlvIC0gT1IpDQoNCiQkDQpPUiA9IFxmcmFje2IgXHRpbWVzIGN9e2EgXHRpbWVzIGR9DQokJA0KKirDnSBuZ2jEqWEqKg0KDQpU4bu3IHPhu5EgY2jDqm5oIMSRbyBsxrDhu51uZyB04bu3IGzhu4cgb2RkcyB44bqjeSByYSBz4buxIGtp4buHbiDhu58gbmjDs20gQSA9IFllcyBzbyB24bubaSBuaMOzbSBBID0gTm8uIFRo4buDIGhp4buHbiB04bu3IHPhu5EgZ2nhu69hIHjDoWMgc3XhuqV0IHjhuqN5IHJhIHbDoCBraMO0bmcgeOG6o3kgcmEgY+G7p2EgbeG7mXQgc+G7sSBraeG7h24gdHJvbmcgbeG7l2kgbmjDs20uDQoNCi0gTuG6v3UgT1I9MTogS2jDtG5nIGPDsyBz4buxIGtow6FjIGJp4buHdCBvZGRzLg0KDQotIE7hur91IE9SPjE6IE9kZHMgc+G7sSBraeG7h24gY2FvIGjGoW4g4bufIG5ow7NtIEEgPSBZZXMuDQoNCi0gTuG6v3UgT1I8MTogT2RkcyBz4buxIGtp4buHbiB0aOG6pXAgaMahbiDhu58gbmjDs20gQSA9IFllcy4NCg0KIyMjIDYuMy4gS2hv4bqjbmcgdGluIGPhuq15DQoNCg0KIyMjIyA2LjMuMS4gUmVsYXRpdmUgUmlzaw0KDQoqKlTDrW5oIGxvZyhSUikqKg0KDQokJA0KXGxvZyhSUikgPSBcbG9nXGxlZnQoXGZyYWN7XGZyYWN7Y317YyArIGR9fXtcZnJhY3thfXthICsgYn19XHJpZ2h0KQ0KJCQNCg0KKipUw61uaCBzYWkgc+G7kSBjaHXhuqluIChTRSkqKg0KDQokJA0KXGxvZyhSUikgPSBcbG9nXGxlZnQoXGZyYWN7XGZyYWN7Y317YyArIGR9fXtcZnJhY3thfXthICsgYn19XHJpZ2h0KQ0KJCQNCg0KKipUw61uaCBraG/huqNuZyB0aW4gY+G6rXkgY2hvIGxvZyhSUikqKg0KDQokJA0KXGxvZyhSUikgXHBtIFpfezEgLSBcYWxwaGEvMn0gXGNkb3QgU0VbXGxvZyhSUildDQokJA0KDQoqKkzhuqV5IG3FqSDEkeG7gyB0cuG7nyBs4bqhaSBSUioqDQoNCiQkDQpDSV97UlJ9ID0gXGxlZnRbIFxleHAoXGxvZyhSUikgLSAxLjk2IFxjZG90IFNFKSxcIFxleHAoXGxvZyhSUikgKyAxLjk2IFxjZG90IFNFKSBccmlnaHRdDQokJA0KVuG7m2kgbeG7qWMgdGluIGPhuq15IDk1JSwgdGjDrCAkWl97MSAtIFxhbHBoYS8yfSA9IDEuOTYkDQoNCg0KIyMjIyA2LjMuMi4gT2RkcyBSYXRpbw0KDQoNCioqVMOtbmggbG9nKE9SKSoqDQoNCiQkDQogXGxvZyhPUik9XGxvZ1xsZWZ0KFxmcmFje2IgXGNkb3QgY317YSBcY2RvdCBkfVxyaWdodCkNCiQkDQoNCg0KKipUw61uaCBzYWkgc+G7kSBjaHXhuqluIChTRSkqKg0KDQokJA0KU0VbXGxvZyhPUildID0gXHNxcnR7XGZyYWN7MX17YX0gKyBcZnJhY3sxfXtifSArIFxmcmFjezF9e2N9ICsgXGZyYWN7MX17ZH19DQokJA0KDQoqKlTDrW5oIGtob+G6o25nIHRpbiBj4bqteSBjaG8gbG9nKE9SKSoqDQoNCiQkDQpcbG9nKFJSKSBccG0gWl97MSAtIFxhbHBoYS8yfSBcY2RvdCBTRVtcbG9nKFJSKV0NCiQkDQoNCioqTOG6pXkgbcWpIMSR4buDIHRy4bufIGzhuqFpIE9SKioNCg0KJCQNCkNJX3tPUn0gPSBcbGVmdFsgXGV4cFxiaWcoXGxvZyhPUikgLSAxLjk2IFx0aW1lcyBTRVxiaWcpLCBccXVhZCBcZXhwXGJpZyhcbG9nKE9SKSArIDEuOTYgXHRpbWVzIFNFXGJpZykgXHJpZ2h0XQ0KJCQNCg0KVuG7m2kgbeG7qWMgdGluIGPhuq15IDk1JSwgdGjDrCAkWl97MSAtIFxhbHBoYS8yfSA9IDEuOTYkDQoNCiMjIyA2LjQuIEdlbmRlciB2w6AgSG9tZW93bmVyDQoNCmBgYHtyfQ0KIyBC4bqjbmcgY2jDqW8gZ2nhu69hIEdlbmRlciB2w6AgSG9tZW93bmVyDQpjaGVvIDwtIHRhYmxlKGR0JEdlbmRlciwgZHQkSG9tZW93bmVyKQ0KYWRkbWFyZ2lucyhjaGVvKQ0KYGBgDQoNCiRwXzE9UChIb21lb3duZXI9WeKIo0dlbmRlcj1GKSh04bu3IGzhu4cgbuG7ryBz4bufIGjhu691IG5ow6ApJA0KDQokcF8yPVAoSG9tZW93bmVyPVniiKNHZW5kZXI9TSkodOG7tyBs4buHIG5hbSBz4bufIGjhu691IG5ow6ApJA0KDQpUw61uaCBoaeG7h3UgaGFpIHThu7cgbOG7hzoNCg0KJCRkPXBfMeKIknBfMiQkDQpLaeG7g20gxJHhu4tuaDoNCg0KSOKCgDogcOKCgSAtIHDigoIgPSAwICh04bu3IGzhu4cgbuG7ryBz4bufIGjhu691IG5ow6AgYuG6sW5nIHThu7cgbOG7hyBuYW0gc+G7nyBo4buvdSBuaMOgKQ0KDQpI4oKBOiBw4oKBIC0gcOKCgiA8IDAgKFThu7cgbOG7hyBu4buvIHPhu58gaOG7r3UgbmjDoCBuaOG7jyBoxqFuIHThu7cgbOG7hyBuYW0gc+G7nyBo4buvdSBuaMOgICkNCg0KDQpgYGB7cn0NCmNvdW50cyA8LSBjKGNoZW9bIkYiLCAiWSJdLCBjaGVvWyJNIiwgIlkiXSkgICAjIFPhu5EgbmfGsOG7nWkgc+G7nyBo4buvdSBuaMOgIHRoZW8gdOG7q25nIGdp4bubaSB0w61uaA0KdG90YWxzIDwtIGMoc3VtKGNoZW9bIkYiLCBdKSwgc3VtKGNoZW9bIk0iLCBdKSkgICMgVOG7lW5nIHPhu5EgbmfGsOG7nWkgdGhlbyB04burbmcgZ2nhu5tpIHTDrW5oDQoNCnRlc3QgPC0gcHJvcC50ZXN0KGNvdW50cywgdG90YWxzLCBhbHRlcm5hdGl2ZSA9ICJsZXNzIiwgY29ycmVjdCA9IEZBTFNFKQ0KdGVzdA0KYGBgDQoNCi0gcHJvcCAxIChwMSkgPSAwLjYwNTk6IHThu7cgbOG7hyBu4buvIHPhu58gaOG7r3UgbmjDoCBraG/huqNuZyA2MC41OSUNCg0KLSBwcm9wIDIgKHAyKSA9IDAuNTk1MjogdOG7tyBs4buHIG5hbSBz4bufIGjhu691IG5ow6Aga2hv4bqjbmcgNTkuNTIlDQoNCi0gUC12YWx1ZSA9IDAuOTAyNQ0KDQpW4bubaSBnacOhIHRy4buLIHAtdmFsdWUgMC45MDI1LCBs4bubbiBoxqFuIG3hu6ljIMO9IG5naMSpYSAwLjA1LiBEbyDEkcOzLCBraMO0bmcgxJHhu6cgYuG6sW5nIGNo4bupbmcgxJHhu4MgYsOhYyBi4buPIGdp4bqjIHRodXnhur90IEjigoANCg0KxJBp4buBdSBuw6B5IGPDsyBuZ2jEqWEgbMOgLCBraMO0bmcgY8OzIHPhu7Ega2jDoWMgYmnhu4d0IGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ogZ2nhu69hIHThu7cgbOG7hyBz4bufIGjhu691IG5ow6AgY+G7p2EgbuG7ryB2w6AgbmFtLCB2w6Aga2jDtG5nIGPDsyBi4bqxbmcgY2jhu6luZyBjaG8gdGjhuqV5IHThu7cgbOG7hyBu4buvIHPhu58gaOG7r3UgbmjDoCB0aOG6pXAgaMahbiB04bu3IGzhu4cgbmFtLg0KDQoNCg0KDQoNCg==