Tên: Mai Huy
MSSV: 43.01.104.065
Số thứ tự: 08
1) Bagging
# Cài thư viện Random Forest
install.packages("randomForest")
# Load thư viện random forest
library(randomForest)
randomForest 4.6-14
Type rfNews() to see new features/changes/bug fixes.
# Load thư viện Mass
library(MASS)
# Load thư viện tree
library(tree)
# set.seed dùng để tái tạo những vector random giống nhau theo tương ứng với giá trị được đưa vào hàm seed
set.seed (1)
# CHia random tập train với số lượng quan sát được chia đôi trong dữ liệu Boston
train = sample(1:nrow(Boston), nrow(Boston)/2)
# Tạo mô hình randomforest để dự đoan giá trị medv là biến đầu ra và lấy tất cả các biến còn lại làm biến đầu vào trong tập train đã chia của dữ liệu Boston
bag.boston = randomForest(medv~.,data=Boston,subset =
train, mtry = 13,importance =TRUE)
- mtry =13: có 13 yếu tố đầu vào được sử dụng cho mỗi lúc phân chia cây
- importance =TRUE: Xuất ra độ quan trọng các biến
# Thông tin mô hình vừa xây dựng
bag.boston
Call:
randomForest(formula = medv ~ ., data = Boston, mtry = 13, importance = TRUE, subset = train)
Type of random forest: regression
Number of trees: 500
No. of variables tried at each split: 13
Mean of squared residuals: 11.33119
% Var explained: 85.26
- Loại mô hình : Regression (ước tính giá trị)
- Số lượng biến mỗi lần phân chia nhánh : 13
- Trung bình bình phương residual của tập train là 11.33119
- Tỉ lệ dự đoán chính xác của out-of-bag cho tập train là 85.26
# Đưa ra dự đoán trên tập test nằm ngoài tập train, biến yhat.bag trả về các giá trị dự đoán trên tập test
yhat.bag = predict(bag.boston,newdata=Boston[-train,])
# Trích xuất kết quả giá trị output medv thật của tập test
boston.test = Boston[-train , "medv"]
# Hiển thị biểu đồ phân tán với cột x là giá trị được dự đoán và cột y là giá trị thật của tập test
plot(yhat.bag, boston.test)
# Gốc toạ độ được chọn 0 ở trục hoành, 1 ở trục tung
abline(0,1)

# Tính Mean Squared Error (MSE) giữa giá trị dự đoán và giá trị thật
mean((yhat.bag-boston.test)^2)
[1] 23.4579
MSE của tập test là 23.4579
# Tạo mô hình randomforest để dự đoan giá trị medv là biến đầu ra và lấy tất cả các biến còn lại làm biến đầu vào trong tập train đã chia của dữ liệu Boston
bag.boston=randomForest(medv~.,data=Boston,subset=train, mtry=13,ntree=25)
- mtry =13: có 13 yếu tố đầu vào được sử dụng cho mỗi lúc phân chia cây
- ntree = 25: số lượng cây là 25
# Đưa ra dự đoán trên tập test nằm ngoài tập train, biến yhat.bag trả về các giá trị dự đoán trên tập test
yhat.bag = predict(bag.boston,newdata=Boston[-train,])
# Tính Mean Squared Error (MSE) giữa giá trị dự đoán và giá trị thật
mean((yhat.bag-boston.test)^2)
[1] 22.99145
MSE của tập test là 22.99145
# set.seed dùng để tái tạo những vector random giống nhau theo tương ứng với giá trị được đưa vào hàm seed
set.seed(1)
# Tạo mô hình randomforest để dự đoan giá trị medv là biến đầu ra và lấy tất cả các biến còn lại làm biến đầu vào trong tập train đã chia của dữ liệu Boston
rf.boston=randomForest(medv~.,data=Boston,subset=train,mtry=6,importance=TRUE)
- mtry =6 : có 6 yếu tố đầu vào được sử dụng cho mỗi lúc phân chia cây
- importance =TRUE: Xuất ra độ quan trọng các biến
# Đưa ra dự đoán trên tập test nằm ngoài tập train, biến yhat.bag trả về các giá trị dự đoán trên tập test
yhat.rf = predict(rf.boston,newdata=Boston[-train,])
# Tính Mean Squared Error (MSE) giữa giá trị dự đoán và giá trị thật
mean((yhat.rf-boston.test)^2)
[1] 19.62021
MSE của tập test là 19.62021
# Hiển thị mức độ quan trọng của các biến
importance(rf.boston)
%IncMSE IncNodePurity
crim 16.697017 1076.08786
zn 3.625784 88.35342
indus 4.968621 609.53356
chas 1.061432 52.21793
nox 13.518179 709.87339
rm 32.343305 7857.65451
age 13.272498 612.21424
dis 9.032477 714.94674
rad 2.878434 95.80598
tax 9.118801 364.92479
ptratio 8.467062 823.93341
black 7.579482 275.62272
lstat 27.129817 6027.63740
%IncMSE là mức độ quan trọng của biến dựa trên sự giảm trung bình trong độ chính xác dự đoán được đưa ra từ lớp out of bag
IncNodePurity là tổng các giá trị giảm trong các node được chia, của tất cả các cây
# Plot biểu đồ thể hiện độ quan trọng của các biến
varImpPlot(rf.boston)

2) Booting
# Load thư viện gbm
library(gbm)
Loaded gbm 2.1.5
# set.seed dùng để tái tạo những vector random giống nhau theo tương ứng với giá trị được đưa vào hàm seed
set.seed(1)
# Tạo mô hình hồi quy ổng quan boosting với biến đầu ra là medv, biến đầu vào là tất cả các biến còn lại, với số lượng cây là 5000, độ sâu tối đa của cây là 4, mô hình theo phân phối gaussian
boost.boston=gbm(medv~.,data=Boston[train,],distribution="gaussian",n.trees=5000,interaction.depth=4)

Hàm summary() cho chúng ta một bảng và 1 biểu đồ cho biết mức độ ảnh hưởng của các biến, ta thấy biến rm có mức độ ảnh hưởng cao nhất là 43.9919329
#Biểu đồ phân tán của các biến dữ liệu với các dữ liệu cột được chia thành cửa sổ 1x2 bằng cách sử dụng hàm mfrow=c(1,2)
par(mfrow=c(1,2))
# Biểu đồ biểu diễn mức độ ảnh hưởng của biến rm
plot(boost.boston,i="rm")

# Biểu đồ biểu diễn mức độ ảnh hưởng của biến lstat
plot(boost.boston,i="lstat")

# Tiến hành dự đoán tập test nằm ngoài tập train với số lượng cây = 5000
yhat.boost=predict(boost.boston,newdata=Boston[-train,],n.trees=5000)
# Tính Mean Squared Error (MSE) giữa giá trị dự đoán và giá trị thật
mean((yhat.boost-boston.test)^2)
[1] 18.84709
MSE của tập test là 18.84709
# Tạo mô hình hồi quy ổng quan boosting với biến đầu ra là medv, biến đầu vào là tất cả các biến còn lại
boost.boston=gbm(medv~.,data=Boston[train,],distribution="gaussian",n.trees=5000,interaction.depth=4,shrinkage=0.2,verbose=F)
- Mô hình có số lượng cây là 5000, phân phối gaussian, độ sâu thấp nhất là 4, learning rate mỗi cây là 0.2, verbose = F: không in ra quá trình huấn luyện mô hình
# Tiến hành dự đoán tập test nằm ngoài tập train với số lượng cây = 5000
yhat.boost=predict(boost.boston,newdata=Boston[-train,],n.trees=5000)
# Tính Mean Squared Error (MSE) giữa giá trị dự đoán và giá trị thật
mean((yhat.boost-boston.test)^2)
[1] 18.33455
MSE của tập test là 18.33455
LS0tDQp0aXRsZTogIkJhZ2dpbmctUmFuZG9tIEZvcmVzdC1Cb29zdGluZy1UdeG6p24gMTAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgVMOqbjogTWFpIEh1eQ0KIyMjIE1TU1Y6IDQzLjAxLjEwNC4wNjUNCiMjIyBT4buRIHRo4bupIHThu7E6IDA4DQoNCg0KIyMgMSkgQmFnZ2luZw0KDQpgYGB7cn0NCiMgQ8OgaSB0aMawIHZp4buHbiBSYW5kb20gRm9yZXN0DQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIHRoxrAgdmnhu4duIHJhbmRvbSBmb3Jlc3QNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KIyBMb2FkIHRoxrAgdmnhu4duIE1hc3MNCmxpYnJhcnkoTUFTUykNCiMgTG9hZCB0aMawIHZp4buHbiB0cmVlDQpsaWJyYXJ5KHRyZWUpDQpgYGANCg0KDQpgYGB7cn0NCiMgc2V0LnNlZWQgZMO5bmcgxJHhu4MgdMOhaSB04bqhbyBuaOG7r25nIHZlY3RvciByYW5kb20gZ2nhu5FuZyBuaGF1IHRoZW8gdMawxqFuZyDhu6luZyB24bubaSBnacOhIHRy4buLIMSRxrDhu6NjIMSRxrBhIHbDoG8gaMOgbSBzZWVkDQpzZXQuc2VlZCAoMSkNCiMgQ0hpYSByYW5kb20gdOG6rXAgdHJhaW4gduG7m2kgc+G7kSBsxrDhu6NuZyBxdWFuIHPDoXQgxJHGsOG7o2MgY2hpYSDEkcO0aSB0cm9uZyBk4buvIGxp4buHdSBCb3N0b24NCnRyYWluID0gc2FtcGxlKDE6bnJvdyhCb3N0b24pLCBucm93KEJvc3RvbikvMikNCiMgVOG6oW8gbcO0IGjDrG5oIHJhbmRvbWZvcmVzdCDEkeG7gyBk4buxIMSRb2FuIGdpw6EgdHLhu4sgbWVkdiBsw6AgYmnhur9uIMSR4bqndSByYSB2w6AgbOG6pXkgdOG6pXQgY+G6oyBjw6FjIGJp4bq/biBjw7JuIGzhuqFpIGzDoG0gYmnhur9uIMSR4bqndSB2w6BvIHRyb25nIHThuq1wIHRyYWluIMSRw6MgY2hpYSBj4bunYSBk4buvIGxp4buHdSBCb3N0b24NCmJhZy5ib3N0b24gPSByYW5kb21Gb3Jlc3QobWVkdn4uLGRhdGE9Qm9zdG9uLHN1YnNldCA9DQp0cmFpbiwgbXRyeSA9IDEzLGltcG9ydGFuY2UgPVRSVUUpDQpgYGANCg0KLSBtdHJ5ID0xMzogY8OzIDEzIHnhur91IHThu5EgxJHhuqd1IHbDoG8gxJHGsOG7o2Mgc+G7rSBk4bulbmcgY2hvIG3hu5dpIGzDumMgcGjDom4gY2hpYSBjw6J5IA0KLSBpbXBvcnRhbmNlID1UUlVFOiBYdeG6pXQgcmEgxJHhu5kgcXVhbiB0cuG7jW5nIGPDoWMgYmnhur9uDQoNCmBgYHtyfQ0KIyBUaMO0bmcgdGluIG3DtCBow6xuaCB24burYSB4w6J5IGThu7FuZw0KYmFnLmJvc3Rvbg0KYGBgDQoNCi0gTG/huqFpIG3DtCBow6xuaCA6IFJlZ3Jlc3Npb24gKMaw4bubYyB0w61uaCBnacOhIHRy4buLKQ0KLSBT4buRIGzGsOG7o25nIGJp4bq/biAgbeG7l2kgbOG6p24gcGjDom4gY2hpYSBuaMOhbmggOiAxMw0KLSBUcnVuZyBiw6xuaCBiw6xuaCBwaMawxqFuZyByZXNpZHVhbCBj4bunYSB04bqtcCB0cmFpbiBsw6AgMTEuMzMxMTkNCi0gVOG7iSBs4buHIGThu7EgxJFvw6FuIGNow61uaCB4w6FjIGPhu6dhIG91dC1vZi1iYWcgY2hvIHThuq1wIHRyYWluIGzDoCA4NS4yNg0KDQpgYGB7cn0NCiMgxJDGsGEgcmEgZOG7sSDEkW/DoW4gdHLDqm4gdOG6rXAgdGVzdCBu4bqxbSBuZ2/DoGkgdOG6rXAgdHJhaW4sIGJp4bq/biB5aGF0LmJhZyB0cuG6oyB24buBIGPDoWMgZ2nDoSB0cuG7iyBk4buxIMSRb8OhbiB0csOqbiB04bqtcCB0ZXN0DQp5aGF0LmJhZyA9IHByZWRpY3QoYmFnLmJvc3RvbixuZXdkYXRhPUJvc3RvblstdHJhaW4sXSkNCmBgYA0KDQpgYGB7cn0NCiMgVHLDrWNoIHh14bqldCBr4bq/dCBxdeG6oyBnacOhIHRy4buLIG91dHB1dCBtZWR2IHRo4bqtdCBj4bunYSB04bqtcCB0ZXN0DQpib3N0b24udGVzdCA9IEJvc3RvblstdHJhaW4gLCAibWVkdiJdDQpgYGANCg0KYGBge3J9DQojIEhp4buDbiB0aOG7iyBiaeG7g3UgxJHhu5MgcGjDom4gdMOhbiB24bubaSBj4buZdCB4IGzDoCBnacOhIHRy4buLIMSRxrDhu6NjIGThu7EgxJFvw6FuIHbDoCBj4buZdCB5IGzDoCBnacOhIHRy4buLIHRo4bqtdCBj4bunYSB04bqtcCB0ZXN0DQpwbG90KHloYXQuYmFnLCBib3N0b24udGVzdCkNCiMgR+G7kWMgdG/huqEgxJHhu5kgxJHGsOG7o2MgY2jhu41uIDAg4bufIHRy4bulYyBob8OgbmgsIDEg4bufIHRy4bulYyB0dW5nDQphYmxpbmUoMCwxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUw61uaCBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgZ2nhu69hIGdpw6EgdHLhu4sgZOG7sSDEkW/DoW4gdsOgIGdpw6EgdHLhu4sgdGjhuq10DQptZWFuKCh5aGF0LmJhZy1ib3N0b24udGVzdCleMikNCmBgYA0KDQoNCk1TRSBj4bunYSB04bqtcCB0ZXN0IGzDoCAyMy40NTc5DQoNCmBgYHtyfQ0KIyBU4bqhbyBtw7QgaMOsbmggcmFuZG9tZm9yZXN0IMSR4buDIGThu7EgxJFvYW4gZ2nDoSB0cuG7iyBtZWR2IGzDoCBiaeG6v24gxJHhuqd1IHJhIHbDoCBs4bqleSB04bqldCBj4bqjIGPDoWMgYmnhur9uIGPDsm4gbOG6oWkgbMOgbSBiaeG6v24gxJHhuqd1IHbDoG8gdHJvbmcgdOG6rXAgdHJhaW4gxJHDoyBjaGlhIGPhu6dhIGThu68gbGnhu4d1IEJvc3Rvbg0KYmFnLmJvc3Rvbj1yYW5kb21Gb3Jlc3QobWVkdn4uLGRhdGE9Qm9zdG9uLHN1YnNldD10cmFpbiwgbXRyeT0xMyxudHJlZT0yNSkNCmBgYA0KDQotIG10cnkgPTEzOiBjw7MgMTMgeeG6v3UgdOG7kSDEkeG6p3UgdsOgbyDEkcaw4bujYyBz4butIGThu6VuZyBjaG8gbeG7l2kgbMO6YyBwaMOibiBjaGlhIGPDonkgDQotIG50cmVlID0gMjU6IHPhu5EgbMaw4bujbmcgY8OieSBsw6AgMjUNCg0KYGBge3J9DQojIMSQxrBhIHJhIGThu7EgxJFvw6FuIHRyw6puIHThuq1wIHRlc3QgbuG6sW0gbmdvw6BpIHThuq1wIHRyYWluLCBiaeG6v24geWhhdC5iYWcgdHLhuqMgduG7gSBjw6FjIGdpw6EgdHLhu4sgZOG7sSDEkW/DoW4gdHLDqm4gdOG6rXAgdGVzdA0KeWhhdC5iYWcgPSBwcmVkaWN0KGJhZy5ib3N0b24sbmV3ZGF0YT1Cb3N0b25bLXRyYWluLF0pDQojIFTDrW5oIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBnaeG7r2EgZ2nDoSB0cuG7iyBk4buxIMSRb8OhbiB2w6AgZ2nDoSB0cuG7iyB0aOG6rXQNCm1lYW4oKHloYXQuYmFnLWJvc3Rvbi50ZXN0KV4yKQ0KYGBgDQoNCk1TRSBj4bunYSB04bqtcCB0ZXN0IGzDoCAyMi45OTE0NQ0KDQpgYGB7cn0NCiMgc2V0LnNlZWQgZMO5bmcgxJHhu4MgdMOhaSB04bqhbyBuaOG7r25nIHZlY3RvciByYW5kb20gZ2nhu5FuZyBuaGF1IHRoZW8gdMawxqFuZyDhu6luZyB24bubaSBnacOhIHRy4buLIMSRxrDhu6NjIMSRxrBhIHbDoG8gaMOgbSBzZWVkDQpzZXQuc2VlZCgxKQ0KIyBU4bqhbyBtw7QgaMOsbmggcmFuZG9tZm9yZXN0IMSR4buDIGThu7EgxJFvYW4gZ2nDoSB0cuG7iyBtZWR2IGzDoCBiaeG6v24gxJHhuqd1IHJhIHbDoCBs4bqleSB04bqldCBj4bqjIGPDoWMgYmnhur9uIGPDsm4gbOG6oWkgbMOgbSBiaeG6v24gxJHhuqd1IHbDoG8gdHJvbmcgdOG6rXAgdHJhaW4gxJHDoyBjaGlhIGPhu6dhIGThu68gbGnhu4d1IEJvc3Rvbg0KcmYuYm9zdG9uPXJhbmRvbUZvcmVzdChtZWR2fi4sZGF0YT1Cb3N0b24sc3Vic2V0PXRyYWluLG10cnk9NixpbXBvcnRhbmNlPVRSVUUpDQoNCmBgYA0KDQotIG10cnkgPTYgOiBjw7MgNiB54bq/dSB04buRIMSR4bqndSB2w6BvIMSRxrDhu6NjIHPhu60gZOG7pW5nIGNobyBt4buXaSBsw7pjIHBow6JuIGNoaWEgY8OieSANCi0gaW1wb3J0YW5jZSA9VFJVRTogWHXhuqV0IHJhIMSR4buZIHF1YW4gdHLhu41uZyBjw6FjIGJp4bq/bg0KDQoNCmBgYHtyfQ0KIyDEkMawYSByYSBk4buxIMSRb8OhbiB0csOqbiB04bqtcCB0ZXN0IG7hurFtIG5nb8OgaSB04bqtcCB0cmFpbiwgYmnhur9uIHloYXQuYmFnIHRy4bqjIHbhu4EgY8OhYyBnacOhIHRy4buLIGThu7EgxJFvw6FuIHRyw6puIHThuq1wIHRlc3QNCnloYXQucmYgPSBwcmVkaWN0KHJmLmJvc3RvbixuZXdkYXRhPUJvc3RvblstdHJhaW4sXSkNCiMgVMOtbmggTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIGdp4buvYSBnacOhIHRy4buLIGThu7EgxJFvw6FuIHbDoCBnacOhIHRy4buLIHRo4bqtdA0KbWVhbigoeWhhdC5yZi1ib3N0b24udGVzdCleMikNCmBgYA0KDQpNU0UgY+G7p2EgdOG6rXAgdGVzdCBsw6AgMTkuNjIwMjENCg0KYGBge3J9DQojIEhp4buDbiB0aOG7iyBt4bupYyDEkeG7mSBxdWFuIHRy4buNbmcgY+G7p2EgY8OhYyBiaeG6v24NCmltcG9ydGFuY2UocmYuYm9zdG9uKQ0KDQpgYGANCg0KJUluY01TRSBsw6AgbeG7qWMgxJHhu5kgcXVhbiB0cuG7jW5nIGPhu6dhIGJp4bq/biBk4buxYSB0csOqbiBz4buxIGdp4bqjbSB0cnVuZyBiw6xuaCB0cm9uZyDEkeG7mSBjaMOtbmggeMOhYyBk4buxIMSRb8OhbiDEkcaw4bujYyDEkcawYSByYSB04burIGzhu5twIG91dCBvZiBiYWcNCg0KSW5jTm9kZVB1cml0eSBsw6AgdOG7lW5nIGPDoWMgZ2nDoSB0cuG7iyBnaeG6o20gdHJvbmcgY8OhYyBub2RlIMSRxrDhu6NjIGNoaWEsIGPhu6dhIHThuqV0IGPhuqMgY8OhYyBjw6J5DQoNCmBgYHtyfQ0KIyBQbG90IGJp4buDdSDEkeG7kyB0aOG7gyBoaeG7h24gxJHhu5kgcXVhbiB0cuG7jW5nIGPhu6dhIGPDoWMgYmnhur9uDQp2YXJJbXBQbG90KHJmLmJvc3RvbikNCmBgYA0KDQoNCiMjIDIpIEJvb3RpbmcNCg0KDQoNCmBgYHtyfQ0KIyBMb2FkIHRoxrAgdmnhu4duIGdibQ0KbGlicmFyeShnYm0pDQpgYGANCg0KYGBge3J9DQojIHNldC5zZWVkIGTDuW5nIMSR4buDIHTDoWkgdOG6oW8gbmjhu69uZyB2ZWN0b3IgcmFuZG9tIGdp4buRbmcgbmhhdSB0aGVvIHTGsMahbmcg4bupbmcgduG7m2kgZ2nDoSB0cuG7iyDEkcaw4bujYyDEkcawYSB2w6BvIGjDoG0gc2VlZA0Kc2V0LnNlZWQoMSkNCiMgVOG6oW8gbcO0IGjDrG5oIGjhu5NpIHF1eSDhu5VuZyBxdWFuIGJvb3N0aW5nIHbhu5tpIGJp4bq/biDEkeG6p3UgcmEgbMOgIG1lZHYsIGJp4bq/biDEkeG6p3UgdsOgbyBsw6AgdOG6pXQgY+G6oyBjw6FjIGJp4bq/biBjw7JuIGzhuqFpLCB24bubaSBz4buRIGzGsOG7o25nIGPDonkgbMOgIDUwMDAsIMSR4buZIHPDonUgdOG7kWkgxJFhIGPhu6dhIGPDonkgbMOgIDQsIG3DtCBow6xuaCB0aGVvIHBow6JuIHBo4buRaSBnYXVzc2lhbg0KYm9vc3QuYm9zdG9uPWdibShtZWR2fi4sZGF0YT1Cb3N0b25bdHJhaW4sXSxkaXN0cmlidXRpb249ImdhdXNzaWFuIixuLnRyZWVzPTUwMDAsaW50ZXJhY3Rpb24uZGVwdGg9NCkNCmBgYA0KDQpgYGB7cn0NCiMgUGjDom4gdMOtY2ggbcO0IGjDrG5oIGJvb3N0aW5nIMSRxrDhu6NjIHThuqFvDQpzdW1tYXJ5KGJvb3N0LmJvc3RvbikNCmBgYA0KDQpIw6BtIHN1bW1hcnkoKSBjaG8gY2jDum5nIHRhIG3hu5l0IGLhuqNuZyB2w6AgMSBiaeG7g3UgxJHhu5MgY2hvIGJp4bq/dCBt4bupYyDEkeG7mSDhuqNuaCBoxrDhu59uZyBj4bunYSBjw6FjIGJp4bq/biwgdGEgdGjhuqV5IGJp4bq/biBybSBjw7MgbeG7qWMgxJHhu5kg4bqjbmggaMaw4bufbmcgY2FvIG5o4bqldCBsw6AgNDMuOTkxOTMyOQ0KDQoNCmBgYHtyfQ0KI0Jp4buDdSDEkeG7kyBwaMOibiB0w6FuIGPhu6dhIGPDoWMgYmnhur9uICBk4buvIGxp4buHdSB24bubaSBjw6FjIGThu68gbGnhu4d1IGPhu5l0IMSRxrDhu6NjIGNoaWEgdGjDoG5oIGPhu61hIHPhu5UgMXgyIGLhurFuZyBjw6FjaCBz4butIGThu6VuZyBow6BtIG1mcm93PWMoMSwyKSANCnBhcihtZnJvdz1jKDEsMikpDQojIEJp4buDdSDEkeG7kyBiaeG7g3UgZGnhu4VuIG3hu6ljIMSR4buZIOG6o25oIGjGsOG7n25nIGPhu6dhIGJp4bq/biBybQ0KcGxvdChib29zdC5ib3N0b24saT0icm0iKQ0KIyBCaeG7g3UgxJHhu5MgYmnhu4N1IGRp4buFbiBt4bupYyDEkeG7mSDhuqNuaCBoxrDhu59uZyBj4bunYSBiaeG6v24gbHN0YXQNCnBsb3QoYm9vc3QuYm9zdG9uLGk9ImxzdGF0IikNCmBgYA0KDQpgYGB7cn0NCiMgVGnhur9uIGjDoG5oIGThu7EgxJFvw6FuIHThuq1wIHRlc3QgbuG6sW0gbmdvw6BpIHThuq1wIHRyYWluIHbhu5tpIHPhu5EgbMaw4bujbmcgY8OieSA9IDUwMDANCnloYXQuYm9vc3Q9cHJlZGljdChib29zdC5ib3N0b24sbmV3ZGF0YT1Cb3N0b25bLXRyYWluLF0sbi50cmVlcz01MDAwKQ0KIyBUw61uaCBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgZ2nhu69hIGdpw6EgdHLhu4sgZOG7sSDEkW/DoW4gdsOgIGdpw6EgdHLhu4sgdGjhuq10DQptZWFuKCh5aGF0LmJvb3N0LWJvc3Rvbi50ZXN0KV4yKQ0KYGBgDQoNCk1TRSBj4bunYSB04bqtcCB0ZXN0IGzDoCAgMTguODQ3MDkNCg0KYGBge3J9DQojIFThuqFvIG3DtCBow6xuaCBo4buTaSBxdXkg4buVbmcgcXVhbiBib29zdGluZyB24bubaSBiaeG6v24gxJHhuqd1IHJhIGzDoCBtZWR2LCBiaeG6v24gxJHhuqd1IHbDoG8gbMOgIHThuqV0IGPhuqMgY8OhYyBiaeG6v24gY8OybiBs4bqhaQ0KYm9vc3QuYm9zdG9uPWdibShtZWR2fi4sZGF0YT1Cb3N0b25bdHJhaW4sXSxkaXN0cmlidXRpb249ImdhdXNzaWFuIixuLnRyZWVzPTUwMDAsaW50ZXJhY3Rpb24uZGVwdGg9NCxzaHJpbmthZ2U9MC4yLHZlcmJvc2U9RikNCmBgYA0KDQotIE3DtCBow6xuaCBjw7Mgc+G7kSBsxrDhu6NuZyBjw6J5IGzDoCA1MDAwLCBwaMOibiBwaOG7kWkgZ2F1c3NpYW4sIMSR4buZIHPDonUgdGjhuqVwIG5o4bqldCBsw6AgNCwgbGVhcm5pbmcgcmF0ZSBt4buXaSBjw6J5IGzDoCAwLjIsIHZlcmJvc2UgPSBGOiBraMO0bmcgaW4gcmEgcXXDoSB0csOsbmggaHXhuqVuIGx1eeG7h24gbcO0IGjDrG5oDQoNCmBgYHtyfQ0KIyBUaeG6v24gaMOgbmggZOG7sSDEkW/DoW4gdOG6rXAgdGVzdCBu4bqxbSBuZ2/DoGkgdOG6rXAgdHJhaW4gduG7m2kgc+G7kSBsxrDhu6NuZyBjw6J5ID0gNTAwMA0KeWhhdC5ib29zdD1wcmVkaWN0KGJvb3N0LmJvc3RvbixuZXdkYXRhPUJvc3RvblstdHJhaW4sXSxuLnRyZWVzPTUwMDApDQojIFTDrW5oIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBnaeG7r2EgZ2nDoSB0cuG7iyBk4buxIMSRb8OhbiB2w6AgZ2nDoSB0cuG7iyB0aOG6rXQNCm1lYW4oKHloYXQuYm9vc3QtYm9zdG9uLnRlc3QpXjIpDQpgYGANCg0KTVNFIGPhu6dhIHThuq1wIHRlc3QgbMOgICAxOC4zMzQ1NQ0K