22:32:56, 20 - 01 - 2024

Nhiệm vụ 2.1


Đọc dữ liệu vào R

File được sử dụng có tên là “Productivity.xls”

library(readxl) # Gọi package "readxl" để sử dụng
Pro <- read_xls("D:\\Rpubs\\Productivity.xls") # Lệnh đọc file xls và gán vào Pro

Thông tin về dữ liệu

is.data.frame(Pro) # Kiểm tra dữ liệu có phải là data frame không
## [1] TRUE

Với kết quả trên thì dữ liệu là data frame

length(Pro) # Xem dữ liệu có bao nhiêu biến
## [1] 10
names(Pro) # Xem tên biến
##  [1] "STATE" "YR"    "P_CAP" "HWY"   "WATER" "UTIL"  "PC"    "GSP"   "EMP"  
## [10] "UNEMP"

Chú thích:

  • STATE: Tên tiểu bang
  • YR: Năm
  • P_CAP: Vốn đầu tư công
  • HWY: Vốn đầu tư đường cao tốc
  • WATER: Vốn đầu tư nhà máy nước
  • UTIL: Vốn tiện ích
  • PC: Vốn tư nhân
  • GSP: Sản lượng của tiểu bang
  • EMP: Nhân dụng
  • UNEMP: Tỷ lệ thất nghiệp

Sau đây ta sử dụng lệnh skim từ package “skimr” để biết thêm vài thông tin chi tiết về dữ liệu

library(skimr) # Gọi ra package "skimr" 
skim(Pro) # Lệnh mô tả dữ liệu
Data summary
Name Pro
Number of rows 816
Number of columns 10
_______________________
Column type frequency:
character 1
numeric 9
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
STATE 0 1 4 14 0 48 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
YR 0 1 1978.00 4.90 1970.00 1974.00 1978.00 1982.00 1986.00 ▇▆▆▆▇
P_CAP 0 1 25036.66 27780.40 2627.12 7096.66 17572.46 27691.57 140217.32 ▇▁▁▁▁
HWY 0 1 10218.42 9253.60 1827.14 3857.86 7556.35 11266.55 47699.42 ▇▂▁▁▁
WATER 0 1 3618.78 4311.74 228.46 764.51 2266.49 4318.70 24592.33 ▇▁▁▁▁
UTIL 0 1 11199.45 14768.87 538.49 2488.33 7008.81 11598.47 80728.14 ▇▂▁▁▁
PC 0 1 58188.29 59770.78 4052.71 21651.39 40671.21 64796.27 375341.60 ▇▁▁▁▁
GSP 0 1 61014.32 69973.90 4354.00 16502.50 39987.00 68126.00 464550.00 ▇▁▁▁▁
EMP 0 1 1747.10 1855.99 108.30 475.02 1164.80 2114.10 11258.00 ▇▂▁▁▁
UNEMP 0 1 6.60 2.23 2.80 5.00 6.20 7.90 18.00 ▇▇▂▁▁

Ta thấy được kết quả ở bảng 1 là tên của data là Pro gồm 816 quan sát và 10 biến. Ở bảng 2 thì ta biết được có 9 biến dạng số(numeric) và 1 biến dạng ký tự(character). Ở bảng 3 cho ta biết được tên của các biến ký tự là “STATE”. Ở bảng 4 cho ta biết được tên các biến dạng số lần lượt là: “YR”, “P_CAP”, “HWY”, “WATER”, “UTIL”, “PC”, “GSP”, “EMP”, “UNEMP”. Trong đó biến “YR” là biến về thời gian nên không thể phép toán.

Ý nghĩa tên các cột của bảng 3 và bảng 4:

  • skim_variable: Tên biến
  • n_missing: Số lượng giá trị thiếu
  • complete_rate: Tỷ lệ hoàn chỉnh
  • mean: Giá trị trung bình
  • sd: Độ lệch chuẩn
  • p0: Min
  • p25: Phân vị thứ nhất
  • p50: Phân vị thứ 2
  • p75: Phân vị thứ 3
  • p100: Max
  • hist: Biểu đồ histogram

Rút trích dữ liệu

Ta tiến hành đổi tên để dễ thực hiện các thao tác

names(Pro) <- c("TB","Năm","Công","CT", "Nước", "Tiện","Tư","SL","HN","TN")# Lệnh đổi tên biến
names(Pro)
##  [1] "TB"   "Năm"  "Công" "CT"   "Nước" "Tiện" "Tư"   "SL"   "HN"   "TN"

Bộ dữ liệu bao gồm 48 nước và được quan sát từ năm 1970 đến năm 1986

a <- table(Pro$TB) # Lệnh xem các tiểu bang được quan sát
dim(a) # Lượng tiểu bang được quan sát
## [1] 48
head(a,4) # Tên 4 tiểu bang đầu được quan sát
## 
##    ALABAMA    ARIZONA   ARKANSAS CALIFORNIA 
##         17         17         17         17
b <- table(Pro$Năm) # Lệnh xem thời điểm quan sát
dim(b) # Thời gian quan sát
## [1] 17

Ta chỉ cần 4 biến là “TB”, “Năm”, “Công”, “SL”

Pro4 <- Pro[,c(1,2,3,8)]

Ta sẽ tạo ra 3 bộ dữ liệu bao gồm:

  • Các quan sát từ bang IDAHO
  • Các quan sát trong năm 1985
  • Các quan sát từ năm 1970 tới năm 1980 và Sản lượng lớn hơn 32000
ida <- Pro4[ Pro4$TB== "IDAHO",] # Lệnh rút ra quan sát từ bang IDAHO
n1985 <- Pro4[Pro4$Năm==1985,] # Lệnh rút ra quan sát trong năm 1985
n7080 <- Pro4[Pro4$Năm>= 1970 & Pro4$Năm <=1980 & Pro4$SL >= 32000,] # Lệnh rút ra quan sát từ năm 1970 tới năm 1980

Ta sẽ tạo ra 1 biến mới là năng suất. Ta sẽ tính sản lượng tạo ra được của mỗi nhân dụng với công thức \(Năng suất = Sản lượng/Nhân dụng\)

NS <- Pro$SL/Pro$HN # Lệnh tạo ra biến mới là năng suất
# Ta ghép biến năng suất vào dữ liệu ban đầu
Pro <- cbind(Pro,NS) # Lệnh ghép các cột lại với nhau

Bây giờ ta sẽ dùng package “tidyverse” để đổi tên và rút trích dữ liệu. Trong package “tidyverse” có toán tử pipe %>% . Chức năng của toán tử pipe là chuyền dữ liệu bên trái làm dữ liệu đầu vào cho bên phải để thực hiện thao tác.

  • Đầu tiên ta gọi package “tidyverse”
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
  • Ta bắt đầu việc đổi tên biến của Pro
Pro1 <- Pro %>% rename(NMN=Nước, ĐTC=Công) # Lệnh đổi tên 
names(Pro1)
##  [1] "TB"   "Năm"  "ĐTC"  "CT"   "NMN"  "Tiện" "Tư"   "SL"   "HN"   "TN"  
## [11] "NS"
  • Sau đó ta sẽ chọn ra những biến cần thiết
Pro2 <- Pro1 %>% select(TB,Năm,ĐTC,NMN,SL) # Lệnh chọn ra biến cần thiết
names(Pro2)
## [1] "TB"  "Năm" "ĐTC" "NMN" "SL"
  • Ta sẽ dùng lệnh filter để lọc ra những quan sát cần thiết

    Các quan sát từ bang IDAHO

ida1 <- Pro2 %>% filter(TB == "IDAHO") # Lệnh chọn ra các quan sát từ IDAHO

Các quan sát trong năm 1985

n19851 <- Pro2 %>% filter(Năm == 1985)# Lệnh chọn ra các quan sát trong năm 1985

Các quan sát từ năm 1970 tới năm 1980 và Sản lượng lớn hơn 32000

n70801 <- Pro2 %>% filter(Năm >= 1970 & Năm <=1980 & SL >= 32000)

Ta sẽ tạo ra 1 năng suất. Ta sẽ tính sản lượng tạo ra được của mỗi nhân dụng với công thức \(Năng suất = Sản lượng/Nhân dụng\) và ghép vào Pro

Pro <- Pro %>% mutate(NS = SL/HN)
head(Pro, 3)
##        TB  Năm     Công      CT    Nước    Tiện       Tư    SL     HN  TN
## 1 ALABAMA 1970 15032.67 7325.80 1655.68 6051.20 35793.80 28418 1010.5 4.7
## 2 ALABAMA 1971 15501.94 7525.94 1721.02 6254.98 37299.91 29375 1021.9 5.2
## 3 ALABAMA 1972 15972.41 7765.42 1764.75 6442.23 38670.30 31303 1072.3 4.7
##         NS
## 1 28.12271
## 2 28.74547
## 3 29.19239

Nhiệm vụ 2.2

Tóm tắt

Bộ dữ liệu điều tra các yếu tố ảnh hưởng đến mức lương của Nhà khoa học dữ liệu.

Giới thiệu

Bộ dữ liệu HR Analytics: Job Change Of Data Scientists là một bộ dữ liệu bảng được lấy từ website:Kaggle. Ta sử dụng bộ dữ liệu này để thực hiện Data manipulation trong nhiệm vụ này.

  • Sử dụng tidyverse để thực hiện Data manipulation

    • Mô tả về bộ dữ liệu.

    • Rút trích các dữ liệu theo yêu cầu

    • Tạo dữ liệu mới

Mô tả dữ liệu

  • Đọc dữ liệu
nv2 <- read.csv("C:\\Users\\a\\Documents\\Zalo Received Files\\vd2.csv")
  • Mô tả dữ liệu

Ta sẽ dùng lệnh str để mô tả cấu trúc dữ liệu

str(nv2)
## 'data.frame':    3755 obs. of  11 variables:
##  $ work_year         : int  2023 2023 2023 2023 2023 2023 2023 2023 2023 2023 ...
##  $ experience_level  : chr  "SE" "MI" "MI" "SE" ...
##  $ employment_type   : chr  "FT" "CT" "CT" "FT" ...
##  $ job_title         : chr  "Principal Data Scientist" "ML Engineer" "ML Engineer" "Data Scientist" ...
##  $ salary            : int  80000 30000 25500 175000 120000 222200 136000 219000 141000 147100 ...
##  $ salary_currency   : chr  "EUR" "USD" "USD" "USD" ...
##  $ salary_in_usd     : int  85847 30000 25500 175000 120000 222200 136000 219000 141000 147100 ...
##  $ employee_residence: chr  "ES" "US" "US" "CA" ...
##  $ remote_ratio      : int  100 100 100 100 100 0 0 0 0 0 ...
##  $ company_location  : chr  "ES" "US" "US" "CA" ...
##  $ company_size      : chr  "L" "S" "S" "M" ...

Bảng kết quả cho ta biết được

  • Cấu trúc dữ liệu là dataframe
  • Dữ liệu bao gồm 3755 quan sát và 11 biến
  • Dữ liệu bao gồm 7 biến định tính và 4 biến định lượng
  • Ý Nghĩa tên biến
    • Work_year: Năm lương được trả
    • Experience_level: Mức độ kinh nghiệm trong công việc trong năm EN > Sơ cấp/ Sơ cấp MI > Trung cấp/ Trung cấp SE > Cao cấp/ Chuyên gia EX > Cấp điều hành/ Giám đốc Employment_type: Loại công việc cho vai trò này PT > Bán thời gian FT > Toàn thời gian CT > Hợp đồng *FL > Làm nghề tự do
    • Job_title: Vai trò này đã được thực hiện trong năm Salary: Tổng số tiền lương gộp được trả Salary_curency: Đơn vị tiền tệ của tiền lương được trả dưới dạng mã tiền tệ Salary_in_usd: Lương tính bằng USD Employee_residence: Quốc gia cư trú chính của nhân viên trong năm làm việc là mã quốc gia Remote_ratio: Tổng khối lượng công việc được thực hiện từ xa Company_location: Quốc gia của văn phòng chính phủ hoặc chi nhánh hợp đồng của người sử dụng lao động *Company_size: Quy mô công ty

Ta sẽ xem bộ dữ liệu này có giá trị thiếu hay không

sum(is.na(nv2))
## [1] 0

Vậy dữ liệu không có giá trị thiếu ## Rút trích dữ liệu Ta sẽ chọn ra 7 biến đó là: “work_year”, “experience_level” ,“employment_type”, “job_title”, “Salary_in_usd”, “Employee_residence”, “Remote_ratio”. Và sẽ tạo ra những bộ dữ liệu nhỏ khác để phân tích. Bao gồm:

  • Các quan sát là nhân viên trung cấp và cao cấp
  • Các quan sát là kỹ sư máy học hoặc nhà khoa học dữ liệu là nhân viên cấp cao
  • Các quan sát có mức lương trên 150000 USD mỗi năm
  • Các quan sát là kỹ sư máy học ở Canada
library(tidyverse) # Gọi package tidyverse
# Thực hiện chọn các biến
nv27 <- nv2 %>% select(work_year,experience_level,employment_type,job_title,salary_in_usd,employee_residence,remote_ratio)
# Rút các quan sát là nhân viên trung cấp và cao cấp
ms <- nv27 %>% filter(experience_level=="MI"|experience_level=="SE")
# Rút các quan sát là kỹ sư máy học hoặc nhà khoa học dữ liệu là nhân viên cấp cao
meds <- nv27 %>% filter(job_title=="ML Engineer"|job_title=="Data Scientist"|job_title=="Machine Learning Engineer") %>%
                  filter(experience_level=="SE")
# Rút các quan sát có mức lương trên 150000 USD mỗi năm
up1500 <- nv27 %>% filter(salary_in_usd>= 150000)
# Rút các quan sát là kỹ sư máy học ở Canada
caml <- nv27 %>% 
        filter(job_title=="ML Engineer"| job_title=="Machine Learning Engineer") %>% 
        filter(employee_residence == "CA")

Ta sẽ xem qua các bộ dữ liệu

# Lệnh xem lượng quan sát của "ms"
dim(ms)
## [1] 3321    7
# Lệnh xem 3 dữ liệu đầu của "ms"
head(ms,3)
##   work_year experience_level employment_type                job_title
## 1      2023               SE              FT Principal Data Scientist
## 2      2023               MI              CT              ML Engineer
## 3      2023               MI              CT              ML Engineer
##   salary_in_usd employee_residence remote_ratio
## 1         85847                 ES          100
## 2         30000                 US          100
## 3         25500                 US          100
#Lệnh xem lượng quan sát của "meds"
dim(meds)
## [1] 838   7
# Lệnh xem 3 dữ liệu đầu của "meds"
head(meds,3)
##   work_year experience_level employment_type      job_title salary_in_usd
## 1      2023               SE              FT Data Scientist        175000
## 2      2023               SE              FT Data Scientist        120000
## 3      2023               SE              FT Data Scientist        219000
##   employee_residence remote_ratio
## 1                 CA          100
## 2                 CA          100
## 3                 CA            0
# Lệnh xem lượng quan sát của "up1500"
dim(up1500)
## [1] 1501    7
# Lệnh xem 3 dữ liệu đầu của "up1500"
head(up1500,3)
##   work_year experience_level employment_type         job_title salary_in_usd
## 1      2023               SE              FT    Data Scientist        175000
## 2      2023               SE              FT Applied Scientist        222200
## 3      2023               SE              FT    Data Scientist        219000
##   employee_residence remote_ratio
## 1                 CA          100
## 2                 US            0
## 3                 CA            0
# Lệnh xem lượng quan sát của "caml"
dim(caml)
## [1] 10  7
# Lệnh xem 3 dữ liệu đầu của " caml"
head(caml,3)
##   work_year experience_level employment_type                 job_title
## 1      2023               SE              FT Machine Learning Engineer
## 2      2023               SE              FT Machine Learning Engineer
## 3      2023               SE              FT               ML Engineer
##   salary_in_usd employee_residence remote_ratio
## 1        115000                 CA          100
## 2         95000                 CA          100
## 3        260000                 CA          100

Tạo biến mới

Ta sẽ tạo ra 1 biến mới là thưởng cho những nhân viên làm việc từ xa. Với công thức tính \(thưởng = (Khối lượng việc/50)*(tiền lương*0.3)/12\)

# Lệnh tạo ra biến mới và ghép vào dữ liệu
nv2 <- nv2 %>% mutate(thưởng = (remote_ratio/50)*(salary_in_usd*0.3)/12)
# Xem 3 dữ liệu đầu của thưởng
head(nv2$thưởng)
## [1] 4292.35 1500.00 1275.00 8750.00 6000.00    0.00
LS0tDQp0aXRsZTogIk5oaeG7h20gduG7pSAyIg0KYXV0aG9yOiAiTMOibSBUaOG6o28gTXkiDQpkYXRlOiAiMjAyNC0wMS0xOSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUcnVlDQogICAgdG9jX2Zsb2F0OiBUcnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogDQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4DQotLS0NCmByIGZvcm1hdChTeXMudGltZSgpLCAnJUg6JU06JVMsICVkIC0gJW0gLSAlWScpYA0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGAgDQoNCiMgKipOaGnhu4dtIHbhu6UgMi4xKioNCg0KKioqDQoNCiMjIMSQ4buNYyBk4buvIGxp4buHdSB2w6BvIFINCg0KRmlsZSDEkcaw4bujYyBz4butIGThu6VuZyBjw7MgdMOqbiBsw6AgIlByb2R1Y3Rpdml0eS54bHMiDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyZWFkeGwpICMgR+G7jWkgcGFja2FnZSAicmVhZHhsIiDEkeG7gyBz4butIGThu6VuZw0KUHJvIDwtIHJlYWRfeGxzKCJEOlxcUnB1YnNcXFByb2R1Y3Rpdml0eS54bHMiKSAjIEzhu4duaCDEkeG7jWMgZmlsZSB4bHMgdsOgIGfDoW4gdsOgbyBQcm8NCmBgYA0KIyMgVGjDtG5nIHRpbiB24buBIGThu68gbGnhu4d1DQpgYGB7cn0NCmlzLmRhdGEuZnJhbWUoUHJvKSAjIEtp4buDbSB0cmEgZOG7ryBsaeG7h3UgY8OzIHBo4bqjaSBsw6AgZGF0YSBmcmFtZSBraMO0bmcNCmBgYA0KVuG7m2kga+G6v3QgcXXhuqMgdHLDqm4gdGjDrCBk4buvIGxp4buHdSBsw6AgZGF0YSBmcmFtZQ0KYGBge3J9DQpsZW5ndGgoUHJvKSAjIFhlbSBk4buvIGxp4buHdSBjw7MgYmFvIG5oacOqdSBiaeG6v24NCm5hbWVzKFBybykgIyBYZW0gdMOqbiBiaeG6v24NCmBgYA0KQ2jDuiB0aMOtY2g6IA0KDQoqICoqU1RBVEUqKjogVMOqbiB0aeG7g3UgYmFuZw0KKiAqKllSKio6IE7Eg20NCiogKipQX0NBUCoqOiBW4buRbiDEkeG6p3UgdMawIGPDtG5nDQoqICoqSFdZKio6IFbhu5FuIMSR4bqndSB0xrAgxJHGsOG7nW5nIGNhbyB04buRYw0KKiAqKldBVEVSKio6IFbhu5FuIMSR4bqndSB0xrAgbmjDoCBtw6F5IG7GsOG7m2MNCiogKipVVElMKio6IFbhu5FuIHRp4buHbiDDrWNoDQoqICoqUEMqKjogVuG7kW4gdMawIG5ow6JuDQoqICoqR1NQKio6IFPhuqNuIGzGsOG7o25nIGPhu6dhIHRp4buDdSBiYW5nDQoqICoqRU1QKio6IE5ow6JuIGThu6VuZw0KKiAqKlVORU1QKio6IFThu7cgbOG7hyB0aOG6pXQgbmdoaeG7h3ANCg0KU2F1IMSRw6J5IHRhIHPhu60gZOG7pW5nIGzhu4duaCBza2ltIHThu6sgcGFja2FnZSAic2tpbXIiIMSR4buDIGJp4bq/dCB0aMOqbSB2w6BpIHRow7RuZyB0aW4gY2hpIHRp4bq/dCB24buBIGThu68gbGnhu4d1DQoNCmBgYHtyIHBhZ2VkLnByaW50PVRSVUV9DQpsaWJyYXJ5KHNraW1yKSAjIEfhu41pIHJhIHBhY2thZ2UgInNraW1yIiANCnNraW0oUHJvKSAjIEzhu4duaCBtw7QgdOG6oyBk4buvIGxp4buHdQ0KYGBgDQoNClRhIHRo4bqleSDEkcaw4bujYyBr4bq/dCBxdeG6oyDhu58gYuG6o25nIDEgbMOgIHTDqm4gY+G7p2EgZGF0YSBsw6AgUHJvIGfhu5NtIDgxNiBxdWFuIHPDoXQgdsOgIDEwIGJp4bq/bi4g4bueIGLhuqNuZyAyIHRow6wgdGEgYmnhur90IMSRxrDhu6NjIGPDsyA5IGJp4bq/biBk4bqhbmcgc+G7kShudW1lcmljKSB2w6AgMSBiaeG6v24gZOG6oW5nIGvDvSB04buxKGNoYXJhY3RlcikuIOG7niBi4bqjbmcgMyBjaG8gdGEgYmnhur90IMSRxrDhu6NjIHTDqm4gY+G7p2EgY8OhYyBiaeG6v24ga8O9IHThu7EgbMOgICJTVEFURSIuIOG7niBi4bqjbmcgNCBjaG8gdGEgYmnhur90IMSRxrDhu6NjIHTDqm4gY8OhYyBiaeG6v24gZOG6oW5nIHPhu5EgbOG6p24gbMaw4bujdCBsw6A6ICJZUiIsICJQX0NBUCIsICJIV1kiLCAiV0FURVIiLCAiVVRJTCIsICJQQyIsICJHU1AiLCAiRU1QIiwgIlVORU1QIi4gVHJvbmcgxJHDsyBiaeG6v24gIllSIiBsw6AgYmnhur9uIHbhu4EgdGjhu51pIGdpYW4gbsOqbiBraMO0bmcgdGjhu4MgcGjDqXAgdG/DoW4uDQoNCsOdIG5naMSpYSB0w6puIGPDoWMgY+G7mXQgY+G7p2EgYuG6o25nIDMgdsOgIGLhuqNuZyA0Og0KDQoqIHNraW1fdmFyaWFibGU6IFTDqm4gYmnhur9uDQoqIG5fbWlzc2luZzogU+G7kSBsxrDhu6NuZyBnacOhIHRy4buLIHRoaeG6v3UNCiogY29tcGxldGVfcmF0ZTogVOG7tyBs4buHIGhvw6BuIGNo4buJbmgNCiogbWVhbjogR2nDoSB0cuG7iyB0cnVuZyBiw6xuaA0KKiBzZDogxJDhu5kgbOG7h2NoIGNodeG6qW4NCiogcDA6IE1pbg0KKiBwMjU6IFBow6JuIHbhu4sgdGjhu6kgbmjhuqV0DQoqIHA1MDogUGjDom4gduG7iyB0aOG7qSAyIA0KKiBwNzU6IFBow6JuIHbhu4sgdGjhu6kgMw0KKiBwMTAwOiBNYXgNCiogaGlzdDogQmnhu4N1IMSR4buTIGhpc3RvZ3JhbQ0KDQojIyBSw7p0IHRyw61jaCBk4buvIGxp4buHdQ0KDQpUYSB0aeG6v24gaMOgbmggxJHhu5VpIHTDqm4gxJHhu4MgZOG7hSB0aOG7sWMgaGnhu4duIGPDoWMgdGhhbyB0w6FjDQpgYGB7cn0NCm5hbWVzKFBybykgPC0gYygiVEIiLCJOxINtIiwiQ8O0bmciLCJDVCIsICJOxrDhu5tjIiwgIlRp4buHbiIsIlTGsCIsIlNMIiwiSE4iLCJUTiIpIyBM4buHbmggxJHhu5VpIHTDqm4gYmnhur9uDQpuYW1lcyhQcm8pDQpgYGANCkLhu5kgZOG7ryBsaeG7h3UgYmFvIGfhu5NtIDQ4IG7GsOG7m2MgdsOgIMSRxrDhu6NjIHF1YW4gc8OhdCB04burIG7Eg20gMTk3MCDEkeG6v24gbsSDbSAxOTg2DQpgYGB7cn0NCmEgPC0gdGFibGUoUHJvJFRCKSAjIEzhu4duaCB4ZW0gY8OhYyB0aeG7g3UgYmFuZyDEkcaw4bujYyBxdWFuIHPDoXQNCmRpbShhKSAjIEzGsOG7o25nIHRp4buDdSBiYW5nIMSRxrDhu6NjIHF1YW4gc8OhdA0KaGVhZChhLDQpICMgVMOqbiA0IHRp4buDdSBiYW5nIMSR4bqndSDEkcaw4bujYyBxdWFuIHPDoXQNCmIgPC0gdGFibGUoUHJvJE7Eg20pICMgTOG7h25oIHhlbSB0aOG7nWkgxJFp4buDbSBxdWFuIHPDoXQNCmRpbShiKSAjIFRo4budaSBnaWFuIHF1YW4gc8OhdA0KYGBgDQpUYSBjaOG7iSBj4bqnbiA0IGJp4bq/biBsw6AgIlRCIiwgIk7Eg20iLCAiQ8O0bmciLCAiU0wiDQpgYGB7cn0NClBybzQgPC0gUHJvWyxjKDEsMiwzLDgpXQ0KYGBgDQpUYSBz4bq9IHThuqFvIHJhIDMgYuG7mSBk4buvIGxp4buHdSBiYW8gZ+G7k206DQoNCiogQ8OhYyBxdWFuIHPDoXQgdOG7qyBiYW5nIElEQUhPDQoqIEPDoWMgcXVhbiBzw6F0IHRyb25nIG7Eg20gMTk4NQ0KKiBDw6FjIHF1YW4gc8OhdCB04burIG7Eg20gMTk3MCB04bubaSBuxINtIDE5ODAgdsOgIFPhuqNuIGzGsOG7o25nIGzhu5tuIGjGoW4gMzIwMDANCg0KDQpgYGB7cn0NCmlkYSA8LSBQcm80WyBQcm80JFRCPT0gIklEQUhPIixdICMgTOG7h25oIHLDunQgcmEgcXVhbiBzw6F0IHThu6sgYmFuZyBJREFITw0KbjE5ODUgPC0gUHJvNFtQcm80JE7Eg209PTE5ODUsXSAjIEzhu4duaCByw7p0IHJhIHF1YW4gc8OhdCB0cm9uZyBuxINtIDE5ODUNCm43MDgwIDwtIFBybzRbUHJvNCROxINtPj0gMTk3MCAmIFBybzQkTsSDbSA8PTE5ODAgJiBQcm80JFNMID49IDMyMDAwLF0gIyBM4buHbmggcsO6dCByYSBxdWFuIHPDoXQgdOG7qyBuxINtIDE5NzAgdOG7m2kgbsSDbSAxOTgwDQpgYGANCg0KVGEgc+G6vSB04bqhbyByYSAxIGJp4bq/biBt4bubaSBsw6AgbsSDbmcgc3XhuqV0LiBUYSBz4bq9IHTDrW5oIHPhuqNuIGzGsOG7o25nIHThuqFvIHJhIMSRxrDhu6NjIGPhu6dhIG3hu5dpIG5ow6JuIGThu6VuZyB24bubaSBjw7RuZyB0aOG7qWMgJE7Eg25nIHN14bqldCA9IFPhuqNuIGzGsOG7o25nL05ow6JuIGThu6VuZyQNCmBgYHtyfQ0KTlMgPC0gUHJvJFNML1BybyRITiAjIEzhu4duaCB04bqhbyByYSBiaeG6v24gbeG7m2kgbMOgIG7Eg25nIHN14bqldA0KIyBUYSBnaMOpcCBiaeG6v24gbsSDbmcgc3XhuqV0IHbDoG8gZOG7ryBsaeG7h3UgYmFuIMSR4bqndQ0KUHJvIDwtIGNiaW5kKFBybyxOUykgIyBM4buHbmggZ2jDqXAgY8OhYyBj4buZdCBs4bqhaSB24bubaSBuaGF1DQpgYGANCkLDonkgZ2nhu50gdGEgc+G6vSBkw7luZyBwYWNrYWdlICJ0aWR5dmVyc2UiIMSR4buDIMSR4buVaSB0w6puIHbDoCByw7p0IHRyw61jaCBk4buvIGxp4buHdS4gVHJvbmcgcGFja2FnZSAidGlkeXZlcnNlIiBjw7MgdG/DoW4gdOG7rSBwaXBlICU+JSAuIENo4bupYyBuxINuZyBj4bunYSB0b8OhbiB04butIHBpcGUgbMOgIGNodXnhu4FuIGThu68gbGnhu4d1IGLDqm4gdHLDoWkgbMOgbSBk4buvIGxp4buHdSDEkeG6p3UgdsOgbyBjaG8gYsOqbiBwaOG6o2kgxJHhu4MgdGjhu7FjIGhp4buHbiB0aGFvIHTDoWMuDQoNCiogxJDhuqd1IHRpw6puIHRhIGfhu41pIHBhY2thZ2UgInRpZHl2ZXJzZSINCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCiogVGEgYuG6r3QgxJHhuqd1IHZp4buHYyDEkeG7lWkgdMOqbiBiaeG6v24gY+G7p2EgUHJvDQpgYGB7cn0NClBybzEgPC0gUHJvICU+JSByZW5hbWUoTk1OPU7GsOG7m2MsIMSQVEM9Q8O0bmcpICMgTOG7h25oIMSR4buVaSB0w6puIA0KbmFtZXMoUHJvMSkNCmBgYA0KKiBTYXUgxJHDsyB0YSBz4bq9IGNo4buNbiByYSBuaOG7r25nIGJp4bq/biBj4bqnbiB0aGnhur90DQpgYGB7cn0NClBybzIgPC0gUHJvMSAlPiUgc2VsZWN0KFRCLE7Eg20sxJBUQyxOTU4sU0wpICMgTOG7h25oIGNo4buNbiByYSBiaeG6v24gY+G6p24gdGhp4bq/dA0KbmFtZXMoUHJvMikNCmBgYA0KKiBUYSBz4bq9IGTDuW5nIGzhu4duaCBmaWx0ZXIgxJHhu4MgbOG7jWMgcmEgbmjhu69uZyBxdWFuIHPDoXQgY+G6p24gdGhp4bq/dA0KDQogICBDw6FjIHF1YW4gc8OhdCB04burIGJhbmcgSURBSE8NCmBgYHtyfQ0KaWRhMSA8LSBQcm8yICU+JSBmaWx0ZXIoVEIgPT0gIklEQUhPIikgIyBM4buHbmggY2jhu41uIHJhIGPDoWMgcXVhbiBzw6F0IHThu6sgSURBSE8NCmBgYA0KICAgQ8OhYyBxdWFuIHPDoXQgdHJvbmcgbsSDbSAxOTg1DQpgYGB7cn0NCm4xOTg1MSA8LSBQcm8yICU+JSBmaWx0ZXIoTsSDbSA9PSAxOTg1KSMgTOG7h25oIGNo4buNbiByYSBjw6FjIHF1YW4gc8OhdCB0cm9uZyBuxINtIDE5ODUNCmBgYA0KICAgQ8OhYyBxdWFuIHPDoXQgdOG7qyBuxINtIDE5NzAgdOG7m2kgbsSDbSAxOTgwIHbDoCBT4bqjbiBsxrDhu6NuZyBs4bubbiBoxqFuIDMyMDAwDQpgYGB7cn0NCm43MDgwMSA8LSBQcm8yICU+JSBmaWx0ZXIoTsSDbSA+PSAxOTcwICYgTsSDbSA8PTE5ODAgJiBTTCA+PSAzMjAwMCkNCmBgYA0KIA0KVGEgc+G6vSB04bqhbyByYSAxIG7Eg25nIHN14bqldC4gVGEgc+G6vSB0w61uaCBz4bqjbiBsxrDhu6NuZyB04bqhbyByYSDEkcaw4bujYyBj4bunYSBt4buXaSBuaMOibiBk4bulbmcgduG7m2kgY8O0bmcgdGjhu6ljICROxINuZyBzdeG6pXQgPSBT4bqjbiBsxrDhu6NuZy9OaMOibiBk4bulbmckIHbDoCBnaMOpcCB2w6BvIFBybw0KYGBge3J9DQpQcm8gPC0gUHJvICU+JSBtdXRhdGUoTlMgPSBTTC9ITikNCmhlYWQoUHJvLCAzKQ0KYGBgDQoNCiMgKipOaGnhu4dtIHbhu6UgMi4yKioNCg0KIyMgVMOzbSB04bqvdA0KQuG7mSBk4buvIGxp4buHdSDEkWnhu4F1IHRyYSBjw6FjIHnhur91IHThu5Eg4bqjbmggaMaw4bufbmcgxJHhur9uIG3hu6ljIGzGsMahbmcgY+G7p2EgTmjDoCBraG9hIGjhu41jIGThu68gbGnhu4d1Lg0KDQojIyBHaeG7m2kgdGhp4buHdSANCkLhu5kgZOG7ryBsaeG7h3UgSFIgQW5hbHl0aWNzOiBKb2IgQ2hhbmdlIE9mIERhdGEgU2NpZW50aXN0cyBsw6AgbeG7mXQgYuG7mSBk4buvIGxp4buHdSBi4bqjbmcgxJHGsOG7o2MgbOG6pXkgdOG7qyB3ZWJzaXRlOltLYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvYXJhc2huaWMvaHItYW5hbHl0aWNzLWpvYi1jaGFuZ2Utb2YtZGF0YS1zY2llbnRpc3RzP3Jlc291cmNlPWRvd25sb2FkKS4gVGEgc+G7rSBk4bulbmcgYuG7mSBk4buvIGxp4buHdSBuw6B5IMSR4buDIHRo4buxYyBoaeG7h24gRGF0YSBtYW5pcHVsYXRpb24gdHJvbmcgbmhp4buHbSB24bulIG7DoHkuIA0KDQoqIFPhu60gZOG7pW5nIHRpZHl2ZXJzZSDEkeG7gyB0aOG7sWMgaGnhu4duIERhdGEgbWFuaXB1bGF0aW9uDQoNCiAgICAqIE3DtCB04bqjIHbhu4EgYuG7mSBk4buvIGxp4buHdS4NCiAgICANCiAgICAqIFLDunQgdHLDrWNoIGPDoWMgZOG7ryBsaeG7h3UgdGhlbyB5w6p1IGPhuqd1IA0KICAgIA0KICAgICogVOG6oW8gZOG7ryBsaeG7h3UgbeG7m2kNCg0KIyMgTcO0IHThuqMgZOG7ryBsaeG7h3UgDQoNCiogxJDhu41jIGThu68gbGnhu4d1IA0KYGBge3J9DQpudjIgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcYVxcRG9jdW1lbnRzXFxaYWxvIFJlY2VpdmVkIEZpbGVzXFx2ZDIuY3N2IikNCmBgYA0KKiBNw7QgdOG6oyBk4buvIGxp4buHdSANCg0KVGEgc+G6vSBkw7luZyBs4buHbmggc3RyIMSR4buDIG3DtCB04bqjIGPhuqV1IHRyw7pjIGThu68gbGnhu4d1DQpgYGB7cn0NCnN0cihudjIpDQpgYGANCkLhuqNuZyBr4bq/dCBxdeG6oyBjaG8gdGEgYmnhur90IMSRxrDhu6NjIA0KDQoqIEPhuqV1IHRyw7pjIGThu68gbGnhu4d1IGzDoCBkYXRhZnJhbWUNCiogROG7ryBsaeG7h3UgYmFvIGfhu5NtIDM3NTUgcXVhbiBzw6F0IHbDoCAxMSBiaeG6v24NCiogROG7ryBsaeG7h3UgYmFvIGfhu5NtIDcgYmnhur9uIMSR4buLbmggdMOtbmggdsOgIDQgYmnhur9uIMSR4buLbmggbMaw4bujbmcNCiogw50gTmdoxKlhIHTDqm4gYmnhur9uDQogICogV29ya195ZWFyOiBOxINtIGzGsMahbmcgxJHGsOG7o2MgdHLhuqMNCiAgKiBFeHBlcmllbmNlX2xldmVsOiBN4bupYyDEkeG7mSBraW5oIG5naGnhu4dtIHRyb25nIGPDtG5nIHZp4buHYyB0cm9uZyBuxINtDQogICAgKkVOID4gU8ahIGPhuqVwLyBTxqEgY+G6pXANCiAgICAqTUkgID4gVHJ1bmcgY+G6pXAvIFRydW5nIGPhuqVwDQogICAgKlNFID4gQ2FvIGPhuqVwLyBDaHV5w6puIGdpYQ0KICAgICpFWCA+IEPhuqVwIMSRaeG7gXUgaMOgbmgvIEdpw6FtIMSR4buRYw0KICAqRW1wbG95bWVudF90eXBlOiBMb+G6oWkgY8O0bmcgdmnhu4djIGNobyB2YWkgdHLDsiBuw6B5DQogICAgKlBUID4gQsOhbiB0aOG7nWkgZ2lhbg0KICAgICpGVCA+IFRvw6BuIHRo4budaSBnaWFuDQogICAgKkNUID4gSOG7o3AgxJHhu5NuZw0KICAgICpGTCA+IEzDoG0gbmdo4buBIHThu7EgZG8NCiAgKiBKb2JfdGl0bGU6IFZhaSB0csOyIG7DoHkgxJHDoyDEkcaw4bujYyB0aOG7sWMgaGnhu4duIHRyb25nIG7Eg20gDQogICpTYWxhcnk6IFThu5VuZyBz4buRIHRp4buBbiBsxrDGoW5nIGfhu5lwIMSRxrDhu6NjIHRy4bqjDQogICpTYWxhcnlfY3VyZW5jeTogxJDGoW4gduG7iyB0aeG7gW4gdOG7hyBj4bunYSB0aeG7gW4gbMawxqFuZyDEkcaw4bujYyB0cuG6oyBkxrDhu5tpIGThuqFuZyBtw6MgdGnhu4FuIHThu4cgDQogICpTYWxhcnlfaW5fdXNkOiBMxrDGoW5nIHTDrW5oIGLhurFuZyBVU0QNCiAgKkVtcGxveWVlX3Jlc2lkZW5jZTogUXXhu5FjIGdpYSBjxrAgdHLDuiBjaMOtbmggY+G7p2EgbmjDom4gdmnDqm4gdHJvbmcgbsSDbSBsw6BtIHZp4buHYyBsw6AgbcOjIHF14buRYyBnaWENCiAgKlJlbW90ZV9yYXRpbzogVOG7lW5nIGto4buRaSBsxrDhu6NuZyBjw7RuZyB2aeG7h2MgxJHGsOG7o2MgdGjhu7FjIGhp4buHbiB04burIHhhIA0KICAqQ29tcGFueV9sb2NhdGlvbjogUXXhu5FjIGdpYSBj4bunYSB2xINuIHBow7JuZyBjaMOtbmggcGjhu6cgaG/hurdjIGNoaSBuaMOhbmggaOG7o3AgxJHhu5NuZyBj4bunYSBuZ8aw4budaSBz4butIGThu6VuZyBsYW8gxJHhu5luZw0KICAqQ29tcGFueV9zaXplOiBRdXkgbcO0IGPDtG5nIHR5DQoNClRhIHPhur0geGVtIGLhu5kgZOG7ryBsaeG7h3UgbsOgeSBjw7MgZ2nDoSB0cuG7iyB0aGnhur91IGhheSBraMO0bmcNCmBgYHtyfQ0Kc3VtKGlzLm5hKG52MikpDQpgYGANClbhuq15IGThu68gbGnhu4d1IGtow7RuZyBjw7MgZ2nDoSB0cuG7iyB0aGnhur91DQojIyBSw7p0IHRyw61jaCBk4buvIGxp4buHdQ0KVGEgc+G6vSBjaOG7jW4gcmEgNyBiaeG6v24gxJHDsyBsw6A6ICJ3b3JrX3llYXIiLCAiZXhwZXJpZW5jZV9sZXZlbCIgLCJlbXBsb3ltZW50X3R5cGUiLCAiam9iX3RpdGxlIiwgIlNhbGFyeV9pbl91c2QiLCAiRW1wbG95ZWVfcmVzaWRlbmNlIiwgIlJlbW90ZV9yYXRpbyIuIFbDoCBz4bq9IHThuqFvIHJhIG5o4buvbmcgYuG7mSBk4buvIGxp4buHdSBuaOG7jyBraMOhYyDEkeG7gyBwaMOibiB0w61jaC4gQmFvIGfhu5NtOg0KDQoqIEPDoWMgcXVhbiBzw6F0IGzDoCBuaMOibiB2acOqbiB0cnVuZyBj4bqlcCB2w6AgY2FvIGPhuqVwDQoqIEPDoWMgcXVhbiBzw6F0IGzDoCBr4bu5IHPGsCBtw6F5IGjhu41jIGhv4bq3YyBuaMOgIGtob2EgaOG7jWMgZOG7ryBsaeG7h3UgbMOgIG5ow6JuIHZpw6puIGPhuqVwIGNhbw0KKiBDw6FjIHF1YW4gc8OhdCBjw7MgbeG7qWMgbMawxqFuZyB0csOqbiAxNTAwMDAgVVNEIG3hu5dpIG7Eg20NCiogQ8OhYyBxdWFuIHPDoXQgbMOgIGvhu7kgc8awIG3DoXkgaOG7jWMg4bufIENhbmFkYQ0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBH4buNaSBwYWNrYWdlIHRpZHl2ZXJzZQ0KIyBUaOG7sWMgaGnhu4duIGNo4buNbiBjw6FjIGJp4bq/bg0KbnYyNyA8LSBudjIgJT4lIHNlbGVjdCh3b3JrX3llYXIsZXhwZXJpZW5jZV9sZXZlbCxlbXBsb3ltZW50X3R5cGUsam9iX3RpdGxlLHNhbGFyeV9pbl91c2QsZW1wbG95ZWVfcmVzaWRlbmNlLHJlbW90ZV9yYXRpbykNCiMgUsO6dCBjw6FjIHF1YW4gc8OhdCBsw6AgbmjDom4gdmnDqm4gdHJ1bmcgY+G6pXAgdsOgIGNhbyBj4bqlcA0KbXMgPC0gbnYyNyAlPiUgZmlsdGVyKGV4cGVyaWVuY2VfbGV2ZWw9PSJNSSJ8ZXhwZXJpZW5jZV9sZXZlbD09IlNFIikNCiMgUsO6dCBjw6FjIHF1YW4gc8OhdCBsw6Aga+G7uSBzxrAgbcOheSBo4buNYyBob+G6t2MgbmjDoCBraG9hIGjhu41jIGThu68gbGnhu4d1IGzDoCBuaMOibiB2acOqbiBj4bqlcCBjYW8NCm1lZHMgPC0gbnYyNyAlPiUgZmlsdGVyKGpvYl90aXRsZT09Ik1MIEVuZ2luZWVyInxqb2JfdGl0bGU9PSJEYXRhIFNjaWVudGlzdCJ8am9iX3RpdGxlPT0iTWFjaGluZSBMZWFybmluZyBFbmdpbmVlciIpICU+JQ0KICAgICAgICAgICAgICAgICAgZmlsdGVyKGV4cGVyaWVuY2VfbGV2ZWw9PSJTRSIpDQojIFLDunQgY8OhYyBxdWFuIHPDoXQgY8OzIG3hu6ljIGzGsMahbmcgdHLDqm4gMTUwMDAwIFVTRCBt4buXaSBuxINtDQp1cDE1MDAgPC0gbnYyNyAlPiUgZmlsdGVyKHNhbGFyeV9pbl91c2Q+PSAxNTAwMDApDQojIFLDunQgY8OhYyBxdWFuIHPDoXQgbMOgIGvhu7kgc8awIG3DoXkgaOG7jWMg4bufIENhbmFkYQ0KY2FtbCA8LSBudjI3ICU+JSANCiAgICAgICAgZmlsdGVyKGpvYl90aXRsZT09Ik1MIEVuZ2luZWVyInwgam9iX3RpdGxlPT0iTWFjaGluZSBMZWFybmluZyBFbmdpbmVlciIpICU+JSANCiAgICAgICAgZmlsdGVyKGVtcGxveWVlX3Jlc2lkZW5jZSA9PSAiQ0EiKQ0KYGBgDQpUYSBz4bq9IHhlbSBxdWEgY8OhYyBi4buZIGThu68gbGnhu4d1IA0KYGBge3J9DQojIEzhu4duaCB4ZW0gbMaw4bujbmcgcXVhbiBzw6F0IGPhu6dhICJtcyINCmRpbShtcykNCiMgTOG7h25oIHhlbSAzIGThu68gbGnhu4d1IMSR4bqndSBj4bunYSAibXMiDQpoZWFkKG1zLDMpDQojTOG7h25oIHhlbSBsxrDhu6NuZyBxdWFuIHPDoXQgY+G7p2EgIm1lZHMiDQpkaW0obWVkcykNCiMgTOG7h25oIHhlbSAzIGThu68gbGnhu4d1IMSR4bqndSBj4bunYSAibWVkcyINCmhlYWQobWVkcywzKQ0KIyBM4buHbmggeGVtIGzGsOG7o25nIHF1YW4gc8OhdCBj4bunYSAidXAxNTAwIg0KZGltKHVwMTUwMCkNCiMgTOG7h25oIHhlbSAzIGThu68gbGnhu4d1IMSR4bqndSBj4bunYSAidXAxNTAwIg0KaGVhZCh1cDE1MDAsMykNCiMgTOG7h25oIHhlbSBsxrDhu6NuZyBxdWFuIHPDoXQgY+G7p2EgImNhbWwiDQpkaW0oY2FtbCkNCiMgTOG7h25oIHhlbSAzIGThu68gbGnhu4d1IMSR4bqndSBj4bunYSAiIGNhbWwiDQpoZWFkKGNhbWwsMykNCmBgYA0KIyMgVOG6oW8gYmnhur9uIG3hu5tpDQpUYSBz4bq9IHThuqFvIHJhIDEgYmnhur9uIG3hu5tpIGzDoCB0aMaw4bufbmcgY2hvIG5o4buvbmcgbmjDom4gdmnDqm4gbMOgbSB2aeG7h2MgdOG7qyB4YS4gVuG7m2kgY8O0bmcgdGjhu6ljIHTDrW5oICR0aMaw4bufbmcgPSAoS2jhu5FpIGzGsOG7o25nIHZp4buHYy81MCkqKHRp4buBbiBsxrDGoW5nKjAuMykvMTIkDQpgYGB7cn0NCiMgTOG7h25oIHThuqFvIHJhIGJp4bq/biBt4bubaSB2w6AgZ2jDqXAgdsOgbyBk4buvIGxp4buHdQ0KbnYyIDwtIG52MiAlPiUgbXV0YXRlKHRoxrDhu59uZyA9IChyZW1vdGVfcmF0aW8vNTApKihzYWxhcnlfaW5fdXNkKjAuMykvMTIpDQojIFhlbSAzIGThu68gbGnhu4d1IMSR4bqndSBj4bunYSB0aMaw4bufbmcNCmhlYWQobnYyJHRoxrDhu59uZykNCmBgYA0KDQoNCg0KDQoNCg==