PHẦN 1: CHUẨN BỊ VÀ TÌM HIỂU DỮ LIỆU

1.1 Đọc dữ liệu

library(readxl)
tcc <- read_excel("D:/download/tcc.xlsx")

1.2 Sơ lược về bộ dữ liệu

Nội dung của bộ dữ liệu là khảo sát quyết định ngưng sử dụng dịch vụ Internet của khách hàng, bao gồm:

  • CustomerID: Mã khách hàng

  • Gender: Giới tính

  • InternetService: Dịch vụ Internet đang sử dụng (Bao gồm Internet cáp quang - Fiber optic và Đường dây thuê bao số - DSL)

  • Contract: Loại hợp đồng (Bao gồm hàng tháng, hàng năm và 2 năm)

  • PaymentMethod: Phương thức thanh toán (Bao gồm séc điện tử, séc gửi qua bưu điện, thẻ tín dụng và chuyển khoản)

  • MonthlyCharges: Phí hàng tháng (Tính bằng USD)

  • Churn1, Churn: Quyết định ngưng sử dụng dịch vụ. (1 là Yes, 0 là No)

Dataset bao gồm 8 biến và 7043 quan sát, không có dữ liệu trống.

PHẦN 2: PHÂN TÍCH VÀ MÔ TẢ CÁC BIẾN ĐỊNH TÍNH

2.1 Gender

Tần số, tần suất

# Lập bảng tần số
gender_f <- table(tcc$gender)

# Lập bảng tần suất
gender_p <- prop.table(gender_f)*100

# Kết hợp thành một bảng
gender <- data.frame(
  Category = names(gender_f),
  Frequency = as.vector(gender_f),
  Percentage = round(as.vector(gender_p), 2)
)
gender

Biểu đồ

ggplot(data = gender, aes(x = '', y = Frequency, fill = Category)) +
  geom_col() +
  coord_polar('y') +
  geom_text(aes(label = percent(Frequency/length(tcc$gender))), position = position_stack(vjust = 0.5)) +
  labs(title = 'Figure 1: Gender of Customers') +
  scale_fill_brewer(palette = "Set3") +
  theme(plot.title = element_text(hjust = 0.5, face = 'bold'))  

Nhận xét: Số lượng khách hàng là nam chiếm khoảng 50.48% và số lượng khách hàng nữ là 49.52% cho thấy rằng số lượng khách hàng nam đang có tỷ lệ cao hơn số khách hàng nữ nhưng sự chênh lệch đó là không đáng kể, chỉ khoảng chưa tới 1%.

2.2 Churn

Tần số, tần suất

# Lập bảng tần số
ch_f <- table(tcc$Churn)

# Lập bảng tần suất
ch_p <- prop.table(ch_f)*100

# Kết hợp thành một bảng
churn <- data.frame(
  Category = names(ch_f),
  Frequency = as.vector(ch_f),
  Percentage = round(as.vector(ch_p), 2)
)
churn

Biểu đồ

ggplot(data = churn, aes(x = '', y = Frequency, fill = Category)) +
  geom_col() +
  coord_polar('y') +
  geom_text(aes(label = percent(Frequency/length(tcc$gender))), position = position_stack(vjust = 0.5)) +
  labs(title = 'Figure 2: Churn of Customers') +
  scale_fill_brewer(palette = "Set3") +
  theme(plot.title = element_text(hjust = 0.5, face = 'bold'))  

Nhận xét: Số lượng khách hàng chọn tiếp tục sử dụng dịch vụ là 73%, khoảng 5174 khách hàng và số khách hàng chọn ngưng sử dụng dịch vụ chiếm 27%, khoảng 1869 khách hàng. Điều này cho thấy rằng số khách hàng đều chọn tiếp tục sử dụng dịch vụ internet chiếm ưu thế so với số khách hàng không sử dụng dịch vụ.

2.3 InternetService

Bảng tần số, tần suất

# Lập bảng tần số
is_f <- table(tcc$InternetService)

# Lập bảng tần suất
is_p <- prop.table(is_f)*100

# Kết hợp thành một bảng
is <- data.frame(
  Category = names(is_f),
  Frequency = as.vector(is_f),
  Percentage = round(as.vector(is_p), 2)
)
is

Biểu đồ

ggplot(data = is, aes(x = '', y = Frequency, fill = Category)) +
  geom_col() +
  coord_polar('y') +
  geom_text(aes(label = percent(Frequency/length(tcc$gender))), position = position_stack(vjust = 0.5)) +
  labs(title = 'Figure 3: Internet Service of Customers') +
  scale_fill_brewer(palette = "Set3") +
  theme(plot.title = element_text(hjust = 0.5, face = 'bold'))  

PHẦN 3: ƯỚC LƯỢNG KHOẢNG VÀ KIỂM ĐỊNH GIẢ THUYẾT CHO TỶ LỆ

3.1 Gender

# Số lượng khách hàng nữ
fm <- sum(tcc$gender == "Female")
# Tổng số khách hàng
total <- length(tcc$gender)
# Kiểm định tỷ lệ
prop.test(fm, total, p = 0.495, conf.level = 0.95)
## 
##  1-sample proportions test with continuity correction
## 
## data:  fm out of total, null probability 0.495
## X-squared = 0.00083849, df = 1, p-value = 0.9769
## alternative hypothesis: true p is not equal to 0.495
## 95 percent confidence interval:
##  0.4835017 0.5069906
## sample estimates:
##         p 
## 0.4952435

Đặt giả thuyết: \[ \left\{ \begin{array}{ll} H_0: & \text{Tỷ lệ thực số khách hàng là nữ } = 0.495 \\ H_1: & \text{Tỷ lệ thực số khách hàng là nữ } \ne 0.495 \end{array} \right. \]

Ta thấy rằng p_value = 0.9769 > 5%, nghĩa là không có đủ cơ sở để bác bỏ H0. Nghĩa là thực tế tỷ lệ số khách hàng nữ là 49.5%, ngoài ra, tỷ lệ khách hàng nữ còn được ước lượng là nằm trong khoảng 48.35-50.7%.

3.2 Churn

# Số lượng No
continue <- sum(tcc$Churn == "No")
# Tổng số khách hàng
total <- length(tcc$gender)
# Kiểm định tỷ lệ
prop.test(continue, total, p = 0.73, conf.level = 0.95)
## 
##  1-sample proportions test with continuity correction
## 
## data:  continue out of total, null probability 0.73
## X-squared = 0.74274, df = 1, p-value = 0.3888
## alternative hypothesis: true p is not equal to 0.73
## 95 percent confidence interval:
##  0.7241207 0.7448820
## sample estimates:
##         p 
## 0.7346301

Đặt giả thuyết: \[ \left\{ \begin{array}{ll} H_0: & \text{Tỷ lệ thực số khách hàng tiếp tục sử dụng dịch vụ } = 0.73 \\ H_1: & \text{Tỷ lệ thực số khách hàng tiếp tục sử dụng dịch vụ } \ne 0.73 \end{array} \right. \]

Ta thấy rằng p_value = 0.3888 > 5%, nghĩa là không có đủ cơ sở để bác bỏ H0. Nghĩa là thực tế tỷ lệ số khách hàng chọn tiếp tục sử dụng dịch vụ là 73%, ngoài ra, tỷ lệ khách hàng tiếp tục sử dụng dịch vụ còn được ước lượng là nằm trong khoảng 72.41-74.48%.

PHẦN 4: PHÂN TÍCH MỐI QUAN HỆ GIỮA CÁC BIẾN

InternetServicechurn

Bảng tần suất chéo

is_churn <- table(tcc$InternetService,tcc$Churn)
is_churn
##              
##                 No  Yes
##   DSL         1962  459
##   Fiber optic 1799 1297
##   No          1413  113

4.2 Biểu đồ

df_is_churn <- as.data.frame(is_churn)
colnames(df_is_churn) <- c("Internet Service","Churn","Freq")
ggplot(df_is_churn, aes(x = Churn, y = Freq, fill = `Internet Service`)) +
  geom_col(position = position_dodge()) +
  labs(title = 'Figure 4', x = 'Churn', y = 'Frequency') +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1),
        plot.title = element_text(hjust = 0.5, face = "bold"))

4.3 Kiểm định Chi-bình phương

chisq.test(is_churn)
## 
##  Pearson's Chi-squared test
## 
## data:  is_churn
## X-squared = 732.31, df = 2, p-value < 2.2e-16

Đặt giả thuyết: \[ \left\{ \begin{array}{ll} H_0: & \text{Không có mối quan hệ giữa hai biến phân loại } \\ H_1: & \text{Có tồn tại mối quan hệ giữa hai biến phân loại } \end{array} \right. \]

Ta thấy kết quả kiểm định cho ra giá trị p_value = 0.000 < 5%, nghĩa là đủ cơ sở để bác bỏ \(H_0\). Vậy mối quan hệ giữa loại dịch vụ và quyết định ngưng sử dụng dịch vụ là có liên quan đến nhau.

4.4 Tính Relative Risk - RR

library(epitools)
rr <- riskratio(is_churn)
rr
## $data
##              
##                 No  Yes Total
##   DSL         1962  459  2421
##   Fiber optic 1799 1297  3096
##   No          1413  113  1526
##   Total       5174 1869  7043
## 
## $measure
##              risk ratio with 95% C.I.
##                estimate     lower     upper
##   DSL         1.0000000        NA        NA
##   Fiber optic 2.2096380 2.0149902 2.4230887
##   No          0.3905764 0.3211862 0.4749579
## 
## $p.value
##              two-sided
##               midp.exact fisher.exact   chi.square
##   DSL                 NA           NA           NA
##   Fiber optic          0 5.462658e-76 1.352046e-73
##   No                   0 1.775062e-25 9.986103e-24
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"

Với nhóm Fiber optic so với DSL, ta có giá trị RR = 2.21 và nằm trong khoảng 2.02-2.42 với độ tin cậy 95%, nghĩa là tỷ lệ khách hàng sử dụng dịch vụ Fiber optic có tỷ lệ ngưng sử dụng dịch vụ cao hơn hẳn so với các khách hàng dùng DSL. Cụ thể, nhóm sử dụng Fiber optic có tỷ lệ dừng sử dụng cao hơn 121% so với nhóm DSL.

Với nhóm không dùng so với DSL, ta có giá trị RR = 0.39 và nằm trong khoảng 0.32-0.47 với độ tin cậy 95%, nghĩa là tỷ lệ khách hàng không dùng dịch vụ có tỷ lệ ngưng sử dụng dịch vụ thấp hơn so với các khách hàng dùng DSL. Cụ thể, nhóm không dùng internet có tỷ lệ dừng sử dụng thấp hơn so với nhóm DSL là 61%.

4.5 Tính Odds Ratio - OR

or <- oddsratio(is_churn)
or
## $data
##              
##                 No  Yes Total
##   DSL         1962  459  2421
##   Fiber optic 1799 1297  3096
##   No          1413  113  1526
##   Total       5174 1869  7043
## 
## $measure
##              odds ratio with 95% C.I.
##                estimate     lower     upper
##   DSL         1.0000000        NA        NA
##   Fiber optic 3.0805710 2.7224910 3.4904113
##   No          0.3422587 0.2743666 0.4236615
## 
## $p.value
##              two-sided
##               midp.exact fisher.exact   chi.square
##   DSL                 NA           NA           NA
##   Fiber optic          0 5.462658e-76 1.352046e-73
##   No                   0 1.775062e-25 9.986103e-24
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"

Ở nhóm Fiber optic, giá trị OR = 3.08 và nằm trong khoảng 2.72-3.49 với độ tin cậy 95% cho thấy việc khách hàng sử dụng Fiber optic có tỷ lệ dừng sử dụng dịch vụ cao hơn gấp 3 lần so với DSL.

Ở nhóm không sử dụng dịch vụ, giá trị OR = 0.34 và nằm trong khoảng 0.27-0.42 với độ tin cậy 95% cho thấy việc khách hàng không sử dụng dịch vụ có tỷ lệ dừng sử dụng ít hơn hẳn so với DSL.

LS0tDQp0aXRsZTogIk5ndXnhu4VuIFRy4bqnbiBLaMOhbmggU2FuXzQiDQphdXRob3I6ICJOZ3V54buFbiBUcuG6p24gS2jDoW5oIFNhbiINCmRhdGU6ICIyMDI1LTA2LTA4Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCi8qIFTEg25nIGPhu6EgY2jhu68gdG/DoG4gYuG7mSB2xINuIGLhuqNuICovDQpib2R5IHsNCiAgZm9udC1zaXplOiAxOHB4Ow0KfQ0KDQovKiBUxINuZyBj4buhIGNo4buvIHRpw6p1IMSR4buBICovDQpoMSB7IGZvbnQtc2l6ZTogMi4yZW07IH0NCmgyIHsgZm9udC1zaXplOiAxLjhlbTsgfQ0KaDMgeyBmb250LXNpemU6IDEuNWVtOyB9DQoNCi8qIFTEg25nIGPhu6EgY2jhu68gxJFv4bqhbiB2xINuICovDQpwIHsNCiAgZm9udC1zaXplOiAxLjFlbTsNCiAgbGluZS1oZWlnaHQ6IDEuNjsgLyogVMSDbmcga2hv4bqjbmcgY8OhY2ggZMOybmcgY2hvIGThu4UgxJHhu41jICovDQp9DQoNCi8qIFTEg25nIGPhu6EgY2jhu68gY29kZSAqLw0KY29kZSB7DQogIGZvbnQtc2l6ZTogMTZweDsNCn0NCmBgYA0KDQojICoqUEjhuqZOIDE6IENIVeG6qE4gQuG7iiBWw4AgVMOMTSBISeG7glUgROG7riBMSeG7hlUqKg0KDQojIyAqKjEuMSDEkOG7jWMgZOG7ryBsaeG7h3UqKg0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHhsKQ0KdGNjIDwtIHJlYWRfZXhjZWwoIkQ6L2Rvd25sb2FkL3RjYy54bHN4IikNCmBgYA0KDQojIyAqKjEuMiBTxqEgbMaw4bujYyB24buBIGLhu5kgZOG7ryBsaeG7h3UqKg0KDQpO4buZaSBkdW5nIGPhu6dhIGLhu5kgZOG7ryBsaeG7h3UgbMOgIGto4bqjbyBzw6F0IHF1eeG6v3QgxJHhu4tuaCBuZ8awbmcgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgSW50ZXJuZXQgY+G7p2Ega2jDoWNoIGjDoG5nLCBiYW8gZ+G7k206DQoNCi0gQ3VzdG9tZXJJRDogTcOjIGtow6FjaCBow6BuZw0KDQotIEdlbmRlcjogR2nhu5tpIHTDrW5oDQoNCi0gSW50ZXJuZXRTZXJ2aWNlOiBE4buLY2ggduG7pSBJbnRlcm5ldCDEkWFuZyBz4butIGThu6VuZyAoQmFvIGfhu5NtIEludGVybmV0IGPDoXAgcXVhbmcgLSBGaWJlciBvcHRpYyB2w6AgxJDGsOG7nW5nIGTDonkgdGh1w6ogYmFvIHPhu5EgLSBEU0wpDQoNCi0gQ29udHJhY3Q6IExv4bqhaSBo4bujcCDEkeG7k25nIChCYW8gZ+G7k20gaMOgbmcgdGjDoW5nLCBow6BuZyBuxINtIHbDoCAyIG7Eg20pDQoNCi0gUGF5bWVudE1ldGhvZDogUGjGsMahbmcgdGjhu6ljIHRoYW5oIHRvw6FuIChCYW8gZ+G7k20gc8OpYyDEkWnhu4duIHThu60sIHPDqWMgZ+G7rWkgcXVhIGLGsHUgxJFp4buHbiwgdGjhursgdMOtbiBk4bulbmcgdsOgIGNodXnhu4NuIGtob+G6o24pDQoNCi0gTW9udGhseUNoYXJnZXM6IFBow60gaMOgbmcgdGjDoW5nIChUw61uaCBi4bqxbmcgVVNEKQ0KDQotIENodXJuMSwgQ2h1cm46IFF1eeG6v3QgxJHhu4tuaCBuZ8awbmcgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UuICgxIGzDoCBZZXMsIDAgbMOgIE5vKQ0KDQpEYXRhc2V0IGJhbyBn4buTbSA4IGJp4bq/biB2w6AgNzA0MyBxdWFuIHPDoXQsIGtow7RuZyBjw7MgZOG7ryBsaeG7h3UgdHLhu5FuZy4NCg0KIyAqKlBI4bqmTiAyOiBQSMOCTiBUw41DSCBWw4AgTcOUIFThuqIgQ8OBQyBCSeG6vk4gxJDhu4pOSCBUw41OSCoqDQoNCiMjICoqMi4xIEdlbmRlcioqDQoNCioqKlThuqduIHPhu5EsIHThuqduIHN14bqldCoqKg0KDQpgYGB7cn0NCiMgTOG6rXAgYuG6o25nIHThuqduIHPhu5ENCmdlbmRlcl9mIDwtIHRhYmxlKHRjYyRnZW5kZXIpDQoNCiMgTOG6rXAgYuG6o25nIHThuqduIHN14bqldA0KZ2VuZGVyX3AgPC0gcHJvcC50YWJsZShnZW5kZXJfZikqMTAwDQoNCiMgS+G6v3QgaOG7o3AgdGjDoG5oIG3hu5l0IGLhuqNuZw0KZ2VuZGVyIDwtIGRhdGEuZnJhbWUoDQogIENhdGVnb3J5ID0gbmFtZXMoZ2VuZGVyX2YpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IoZ2VuZGVyX2YpLA0KICBQZXJjZW50YWdlID0gcm91bmQoYXMudmVjdG9yKGdlbmRlcl9wKSwgMikNCikNCmdlbmRlcg0KYGBgDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzY2FsZXMpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdlbmRlciwgYWVzKHggPSAnJywgeSA9IEZyZXF1ZW5jeSwgZmlsbCA9IENhdGVnb3J5KSkgKw0KICBnZW9tX2NvbCgpICsNCiAgY29vcmRfcG9sYXIoJ3knKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjZW50KEZyZXF1ZW5jeS9sZW5ndGgodGNjJGdlbmRlcikpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgbGFicyh0aXRsZSA9ICdGaWd1cmUgMTogR2VuZGVyIG9mIEN1c3RvbWVycycpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gJ2JvbGQnKSkgIA0KYGBgDQoNCioqKk5o4bqtbiB4w6l0OioqKiBT4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBsw6AgbmFtIGNoaeG6v20ga2hv4bqjbmcgNTAuNDglIHbDoCBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBu4buvIGzDoCA0OS41MiUgY2hvIHRo4bqleSBy4bqxbmcgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbmFtIMSRYW5nIGPDsyB04bu3IGzhu4cgY2FvIGjGoW4gc+G7kSBraMOhY2ggaMOgbmcgbuG7ryBuaMawbmcgc+G7sSBjaMOqbmggbOG7h2NoIMSRw7MgbMOgIGtow7RuZyDEkcOhbmcga+G7gywgY2jhu4kga2hv4bqjbmcgY2jGsGEgdOG7m2kgMSUuDQoNCiMjICoqMi4yIENodXJuKioNCg0KKioqVOG6p24gc+G7kSwgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc+G7kQ0KY2hfZiA8LSB0YWJsZSh0Y2MkQ2h1cm4pDQoNCiMgTOG6rXAgYuG6o25nIHThuqduIHN14bqldA0KY2hfcCA8LSBwcm9wLnRhYmxlKGNoX2YpKjEwMA0KDQojIEvhur90IGjhu6NwIHRow6BuaCBt4buZdCBi4bqjbmcNCmNodXJuIDwtIGRhdGEuZnJhbWUoDQogIENhdGVnb3J5ID0gbmFtZXMoY2hfZiksDQogIEZyZXF1ZW5jeSA9IGFzLnZlY3RvcihjaF9mKSwNCiAgUGVyY2VudGFnZSA9IHJvdW5kKGFzLnZlY3RvcihjaF9wKSwgMikNCikNCmNodXJuDQpgYGANCg0KKioqQmnhu4N1IMSR4buTKioqDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBjaHVybiwgYWVzKHggPSAnJywgeSA9IEZyZXF1ZW5jeSwgZmlsbCA9IENhdGVnb3J5KSkgKw0KICBnZW9tX2NvbCgpICsNCiAgY29vcmRfcG9sYXIoJ3knKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjZW50KEZyZXF1ZW5jeS9sZW5ndGgodGNjJGdlbmRlcikpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgbGFicyh0aXRsZSA9ICdGaWd1cmUgMjogQ2h1cm4gb2YgQ3VzdG9tZXJzJykgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAnYm9sZCcpKSAgDQpgYGANCg0KKioqTmjhuq1uIHjDqXQ6KioqIFPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIGNo4buNbiB0aeG6v3AgdOG7pWMgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgbMOgIDczJSwga2hv4bqjbmcgNTE3NCBraMOhY2ggaMOgbmcgdsOgIHPhu5Ega2jDoWNoIGjDoG5nIGNo4buNbiBuZ8awbmcgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgY2hp4bq/bSAyNyUsIGtob+G6o25nIDE4Njkga2jDoWNoIGjDoG5nLiDEkGnhu4F1IG7DoHkgY2hvIHRo4bqleSBy4bqxbmcgc+G7kSBraMOhY2ggaMOgbmcgxJHhu4F1IGNo4buNbiB0aeG6v3AgdOG7pWMgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgaW50ZXJuZXQgY2hp4bq/bSDGsHUgdGjhur8gc28gduG7m2kgc+G7kSBraMOhY2ggaMOgbmcga2jDtG5nIHPhu60gZOG7pW5nIGThu4tjaCB24bulLg0KDQojIyAqKjIuMyBJbnRlcm5ldFNlcnZpY2UqKg0KDQoqKipC4bqjbmcgdOG6p24gc+G7kSwgdOG6p24gc3XhuqV0KioqDQoNCmBgYHtyfQ0KIyBM4bqtcCBi4bqjbmcgdOG6p24gc+G7kQ0KaXNfZiA8LSB0YWJsZSh0Y2MkSW50ZXJuZXRTZXJ2aWNlKQ0KDQojIEzhuq1wIGLhuqNuZyB04bqnbiBzdeG6pXQNCmlzX3AgPC0gcHJvcC50YWJsZShpc19mKSoxMDANCg0KIyBL4bq/dCBo4bujcCB0aMOgbmggbeG7mXQgYuG6o25nDQppcyA8LSBkYXRhLmZyYW1lKA0KICBDYXRlZ29yeSA9IG5hbWVzKGlzX2YpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IoaXNfZiksDQogIFBlcmNlbnRhZ2UgPSByb3VuZChhcy52ZWN0b3IoaXNfcCksIDIpDQopDQppcw0KYGBgDQoNCioqKkJp4buDdSDEkeG7kyoqKg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gaXMsIGFlcyh4ID0gJycsIHkgPSBGcmVxdWVuY3ksIGZpbGwgPSBDYXRlZ29yeSkpICsNCiAgZ2VvbV9jb2woKSArDQogIGNvb3JkX3BvbGFyKCd5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGVyY2VudChGcmVxdWVuY3kvbGVuZ3RoKHRjYyRnZW5kZXIpKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGxhYnModGl0bGUgPSAnRmlndXJlIDM6IEludGVybmV0IFNlcnZpY2Ugb2YgQ3VzdG9tZXJzJykgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAnYm9sZCcpKSAgDQpgYGANCg0KIyAqKlBI4bqmTiAzOiDGr+G7mkMgTMav4buiTkcgS0hP4bqiTkcgVsOAIEtJ4buCTSDEkOG7ik5IIEdJ4bqiIFRIVVnhur5UIENITyBU4bu2IEzhu4YqKg0KDQojIyAqKjMuMSBHZW5kZXIqKg0KDQpgYGB7cn0NCiMgU+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbuG7rw0KZm0gPC0gc3VtKHRjYyRnZW5kZXIgPT0gIkZlbWFsZSIpDQojIFThu5VuZyBz4buRIGtow6FjaCBow6BuZw0KdG90YWwgPC0gbGVuZ3RoKHRjYyRnZW5kZXIpDQojIEtp4buDbSDEkeG7i25oIHThu7cgbOG7hw0KcHJvcC50ZXN0KGZtLCB0b3RhbCwgcCA9IDAuNDk1LCBjb25mLmxldmVsID0gMC45NSkNCmBgYA0KDQrEkOG6t3QgZ2nhuqMgdGh1eeG6v3Q6IA0KJCQNClxsZWZ0XHsNClxiZWdpbnthcnJheX17bGx9DQpIXzA6ICYgXHRleHR7VOG7tyBs4buHIHRo4buxYyBz4buRIGtow6FjaCBow6BuZyBsw6AgbuG7ryB9ID0gMC40OTUgXFwNCkhfMTogJiBcdGV4dHtU4bu3IGzhu4cgdGjhu7FjIHPhu5Ega2jDoWNoIGjDoG5nIGzDoCBu4buvIH0gXG5lIDAuNDk1DQpcZW5ke2FycmF5fQ0KXHJpZ2h0Lg0KJCQNCg0KVGEgdGjhuqV5IHLhurFuZyBwX3ZhbHVlID0gMC45NzY5ID4gNSUsIG5naMSpYSBsw6Aga2jDtG5nIGPDsyDEkeG7pyBjxqEgc+G7nyDEkeG7gyBiw6FjIGLhu48gSDAuIE5naMSpYSBsw6AgdGjhu7FjIHThur8gdOG7tyBs4buHIHPhu5Ega2jDoWNoIGjDoG5nIG7hu68gbMOgIDQ5LjUlLCBuZ2/DoGkgcmEsIHThu7cgbOG7hyBraMOhY2ggaMOgbmcgbuG7ryBjw7JuIMSRxrDhu6NjIMaw4bubYyBsxrDhu6NuZyBsw6AgbuG6sW0gdHJvbmcga2hv4bqjbmcgNDguMzUtNTAuNyUuDQoNCiMjICoqMy4yIENodXJuKioNCg0KYGBge3J9DQojIFPhu5EgbMaw4bujbmcgTm8NCmNvbnRpbnVlIDwtIHN1bSh0Y2MkQ2h1cm4gPT0gIk5vIikNCiMgVOG7lW5nIHPhu5Ega2jDoWNoIGjDoG5nDQp0b3RhbCA8LSBsZW5ndGgodGNjJGdlbmRlcikNCiMgS2nhu4NtIMSR4buLbmggdOG7tyBs4buHDQpwcm9wLnRlc3QoY29udGludWUsIHRvdGFsLCBwID0gMC43MywgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KxJDhurd0IGdp4bqjIHRodXnhur90OiANCiQkDQpcbGVmdFx7DQpcYmVnaW57YXJyYXl9e2xsfQ0KSF8wOiAmIFx0ZXh0e1Thu7cgbOG7hyB0aOG7sWMgc+G7kSBraMOhY2ggaMOgbmcgdGnhur9wIHThu6VjIHPhu60gZOG7pW5nIGThu4tjaCB24bulIH0gPSAwLjczIFxcDQpIXzE6ICYgXHRleHR7VOG7tyBs4buHIHRo4buxYyBz4buRIGtow6FjaCBow6BuZyB0aeG6v3AgdOG7pWMgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgfSBcbmUgMC43Mw0KXGVuZHthcnJheX0NClxyaWdodC4NCiQkDQoNClRhIHRo4bqleSBy4bqxbmcgcF92YWx1ZSA9IDAuMzg4OCA+IDUlLCBuZ2jEqWEgbMOgIGtow7RuZyBjw7MgxJHhu6cgY8ahIHPhu58gxJHhu4MgYsOhYyBi4buPIEgwLiBOZ2jEqWEgbMOgIHRo4buxYyB04bq/IHThu7cgbOG7hyBz4buRIGtow6FjaCBow6BuZyBjaOG7jW4gdGnhur9wIHThu6VjIHPhu60gZOG7pW5nIGThu4tjaCB24bulIGzDoCA3MyUsIG5nb8OgaSByYSwgdOG7tyBs4buHIGtow6FjaCBow6BuZyB0aeG6v3AgdOG7pWMgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgY8OybiDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgbMOgIG7hurFtIHRyb25nIGtob+G6o25nIDcyLjQxLTc0LjQ4JS4NCg0KIyAqKlBI4bqmTiA0OiBQSMOCTiBUw41DSCBN4buQSSBRVUFOIEjhu4YgR0nhu65BIEPDgUMgQknhur5OKioNCg0KKipgSW50ZXJuZXRTZXJ2aWNlYCB2w6AgYGNodXJuYCoqDQoNCioqKkLhuqNuZyB04bqnbiBzdeG6pXQgY2jDqW8qKioNCg0KYGBge3J9DQppc19jaHVybiA8LSB0YWJsZSh0Y2MkSW50ZXJuZXRTZXJ2aWNlLHRjYyRDaHVybikNCmlzX2NodXJuDQpgYGANCg0KIyMgKio0LjIgQmnhu4N1IMSR4buTKioNCg0KYGBge3J9DQpkZl9pc19jaHVybiA8LSBhcy5kYXRhLmZyYW1lKGlzX2NodXJuKQ0KY29sbmFtZXMoZGZfaXNfY2h1cm4pIDwtIGMoIkludGVybmV0IFNlcnZpY2UiLCJDaHVybiIsIkZyZXEiKQ0KZ2dwbG90KGRmX2lzX2NodXJuLCBhZXMoeCA9IENodXJuLCB5ID0gRnJlcSwgZmlsbCA9IGBJbnRlcm5ldCBTZXJ2aWNlYCkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArDQogIGxhYnModGl0bGUgPSAnRmlndXJlIDQnLCB4ID0gJ0NodXJuJywgeSA9ICdGcmVxdWVuY3knKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkNCmBgYA0KDQojIyAqKjQuMyBLaeG7g20gxJHhu4tuaCBDaGktYsOsbmggcGjGsMahbmcqKg0KDQpgYGB7cn0NCmNoaXNxLnRlc3QoaXNfY2h1cm4pDQpgYGANCsSQ4bq3dCBnaeG6oyB0aHV54bq/dDoNCiQkDQpcbGVmdFx7DQpcYmVnaW57YXJyYXl9e2xsfQ0KSF8wOiAmIFx0ZXh0e0tow7RuZyBjw7MgbeG7kWkgcXVhbiBo4buHIGdp4buvYSBoYWkgYmnhur9uIHBow6JuIGxv4bqhaSB9IFxcDQpIXzE6ICYgXHRleHR7Q8OzIHThu5NuIHThuqFpIG3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgaGFpIGJp4bq/biBwaMOibiBsb+G6oWkgfQ0KXGVuZHthcnJheX0NClxyaWdodC4NCiQkDQoNClRhIHRo4bqleSBr4bq/dCBxdeG6oyBraeG7g20gxJHhu4tuaCBjaG8gcmEgZ2nDoSB0cuG7iyBwX3ZhbHVlID0gMC4wMDAgPCA1JSwgbmdoxKlhIGzDoCDEkeG7pyBjxqEgc+G7nyDEkeG7gyBiw6FjIGLhu48gJEhfMCQuIFbhuq15IG3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgbG/huqFpIGThu4tjaCB24bulIHbDoCBxdXnhur90IMSR4buLbmggbmfGsG5nIHPhu60gZOG7pW5nIGThu4tjaCB24bulIGzDoCBjw7MgbGnDqm4gcXVhbiDEkeG6v24gbmhhdS4NCg0KIyMgKio0LjQgVMOtbmggUmVsYXRpdmUgUmlzayAtIFJSKioNCg0KYGBge3J9DQpsaWJyYXJ5KGVwaXRvb2xzKQ0KcnIgPC0gcmlza3JhdGlvKGlzX2NodXJuKQ0KcnINCmBgYA0KVuG7m2kgbmjDs20gRmliZXIgb3B0aWMgc28gduG7m2kgRFNMLCB0YSBjw7MgZ2nDoSB0cuG7iyBSUiA9IDIuMjEgdsOgIG7hurFtIHRyb25nIGtob+G6o25nIDIuMDItMi40MiB24bubaSDEkeG7mSB0aW4gY+G6rXkgOTUlLCBuZ2jEqWEgbMOgIHThu7cgbOG7hyBraMOhY2ggaMOgbmcgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgRmliZXIgb3B0aWMgY8OzIHThu7cgbOG7hyBuZ8awbmcgc+G7rSBk4bulbmcgZOG7i2NoIHbhu6UgY2FvIGjGoW4gaOG6s24gc28gduG7m2kgY8OhYyBraMOhY2ggaMOgbmcgZMO5bmcgRFNMLiBD4bulIHRo4buDLCBuaMOzbSBz4butIGThu6VuZyBGaWJlciBvcHRpYyBjw7MgdOG7tyBs4buHIGThu6tuZyBz4butIGThu6VuZyBjYW8gaMahbiAxMjElIHNvIHbhu5tpIG5ow7NtIERTTC4NCg0KVuG7m2kgbmjDs20ga2jDtG5nIGTDuW5nIHNvIHbhu5tpIERTTCwgdGEgY8OzIGdpw6EgdHLhu4sgUlIgPSAwLjM5IHbDoCBu4bqxbSB0cm9uZyBraG/huqNuZyAwLjMyLTAuNDcgduG7m2kgxJHhu5kgdGluIGPhuq15IDk1JSwgbmdoxKlhIGzDoCB04bu3IGzhu4cga2jDoWNoIGjDoG5nIGtow7RuZyBkw7luZyBk4buLY2ggduG7pSBjw7MgdOG7tyBs4buHIG5nxrBuZyBz4butIGThu6VuZyBk4buLY2ggduG7pSB0aOG6pXAgaMahbiBzbyB24bubaSBjw6FjIGtow6FjaCBow6BuZyBkw7luZyBEU0wuIEPhu6UgdGjhu4MsIG5ow7NtIGtow7RuZyBkw7luZyBpbnRlcm5ldCBjw7MgdOG7tyBs4buHIGThu6tuZyBz4butIGThu6VuZyB0aOG6pXAgaMahbiBzbyB24bubaSBuaMOzbSBEU0wgbMOgIDYxJS4NCg0KDQojIyAqKjQuNSBUw61uaCBPZGRzIFJhdGlvIC0gT1IqKg0KDQpgYGB7cn0NCm9yIDwtIG9kZHNyYXRpbyhpc19jaHVybikNCm9yDQpgYGANCg0K4bueIG5ow7NtIEZpYmVyIG9wdGljLCBnacOhIHRy4buLIE9SID0gMy4wOCB2w6AgbuG6sW0gdHJvbmcga2hv4bqjbmcgMi43Mi0zLjQ5IHbhu5tpIMSR4buZIHRpbiBj4bqteSA5NSUgY2hvIHRo4bqleSB2aeG7h2Mga2jDoWNoIGjDoG5nIHPhu60gZOG7pW5nIEZpYmVyIG9wdGljIGPDsyB04bu3IGzhu4cgZOG7q25nIHPhu60gZOG7pW5nIGThu4tjaCB24bulIGNhbyBoxqFuIGfhuqVwIDMgbOG6p24gc28gduG7m2kgRFNMLg0KDQrhu54gbmjDs20ga2jDtG5nIHPhu60gZOG7pW5nIGThu4tjaCB24bulLCBnacOhIHRy4buLIE9SID0gMC4zNCB2w6AgbuG6sW0gdHJvbmcga2hv4bqjbmcgMC4yNy0wLjQyIHbhu5tpIMSR4buZIHRpbiBj4bqteSA5NSUgY2hvIHRo4bqleSB2aeG7h2Mga2jDoWNoIGjDoG5nIGtow7RuZyBz4butIGThu6VuZyBk4buLY2ggduG7pSBjw7MgdOG7tyBs4buHIGThu6tuZyBz4butIGThu6VuZyDDrXQgaMahbiBo4bqzbiBzbyB24bubaSBEU0wuDQoNCg0KDQo=