Tên: Mai Huy

MSSV: 43.01.104.065

Số thứ tự: 08

1) The Validation Set Approad

# Load thư viện ISLR
library(ISLR)
# 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)
# 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 1 tập train có 196 quan sát được rút trích ngẫu nhiên từ 392 quan sát ban đầu
train = sample(392 ,196)
# Fit mô hình hồi quy đơn giản để dự đoán biến đầu ra mpg trong tập train
lm.fit=lm(mpg~horsepower,data=Auto,subset=train)
# attach dùng để khiến cho những biến feature trong dữ liệu có sẵn trong Rstudio theo tên
attach(Auto)
The following objects are masked from Auto (pos = 3):

    acceleration, cylinders, displacement, horsepower, mpg, name, origin, weight, year

The following objects are masked from Auto (pos = 4):

    acceleration, cylinders, displacement, horsepower, mpg, name, origin, weight, year

The following objects are masked from Auto (pos = 5):

    acceleration, cylinders, displacement, horsepower, mpg, name, origin, weight, year
# Sử dụng hàm predict để ước lượng giá trị output cho 392 quan sát và sử dụng hàm mean() để tính MSE của 196 quan sát trong tập validation
mean((mpg -predict (lm.fit ,Auto))[-train ]^2)
[1] 23.26601
# Fit mô hình hồi quy bậc 2 để dự đoán biến đầu ra mpg trong tập train
lm.fit2=lm(mpg∼poly(horsepower ,2),data=Auto , subset=train)
# Tính giá trị MSE cho tập validation gồm 196 quan sát
mean((mpg -predict (lm.fit2 ,Auto ))[- train]^2)
[1] 18.71646
# Fit mô hình hồi quy bậc 3 để dự đoán biến đầu ra mpg trong tập train
lm.fit3=lm(mpg∼poly(horsepower ,3),data=Auto , subset=train)
# Tính giá trị MSE cho tập validation gồm 196 quan sát
mean((mpg -predict (lm.fit3 ,Auto ))[- train]^2)
[1] 18.79401

Validation set error cho mô hình hàm bậc 2 và 3 lần lượt là 18.71 và 18.79. Tuy nhiên khi chúng ta có tập training ngẫu nhiên khác, những con số này cũng sẽ thay đổi

# 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(2)
# Tạo 1 tập train có 196 quan sát được rút trích ngẫu nhiên từ 392 quan sát ban đầu
train = sample(392 ,196)
# Fit mô hình hồi quy bậc 1 để dự đoán biến đầu ra mpg trong tập train
lm.fit=lm(mpg∼horsepower ,subset=train)
# Tính giá trị MSE cho tập validation gồm 196 quan sát
mean((mpg -predict (lm.fit ,Auto))[-train ]^2)
[1] 25.72651
# Fit mô hình hồi quy bậc 2 để dự đoán biến đầu ra mpg trong tập train
lm.fit2=lm(mpg∼poly(horsepower ,2),data=Auto , subset=train)
# Tính giá trị MSE cho tập validation gồm 196 quan sát
mean((mpg -predict (lm.fit2 ,Auto ))[- train]^2)
[1] 20.43036
# Fit mô hình hồi quy bậc 3 để dự đoán biến đầu ra mpg trong tập train
 lm.fit3=lm(mpg∼poly(horsepower ,3),data=Auto , subset=train)
# Tính giá trị MSE cho tập validation gồm 196 quan sát
mean((mpg -predict (lm.fit3 ,Auto ))[- train]^2)
[1] 20.38533

Validation set error cho mô hình hồi quy bậc 1,2,3 lần lượt là 25,72, 20.43, 20.38. Chúng ta thấy mô hình bậc 2 cải thiện sai số rất nhiều so với bậc 1 còn mô hình bậc 3 thì sai số cải thiện không quá đáng kể.

2) Leave-One-Out Cross-Validation

# Fit mô hình hồi quy tuyến tính để dự đoán biến đầu ra mpg khi biến đầu vào là horsepower trong tập train
glm.fit = glm(mpg~horsepower ,data= Auto)
# Tính các hệ số B0 và B1 của mô hình
coef(glm.fit)
(Intercept)  horsepower 
 39.9358610  -0.1578447 

Ngoài cách viết trên chúng ta có thể bằng hàm lm() quen thuộc

# Fit mô hình hồi quy tuyến tính để dự đoán biến đầu ra mpg khi biến đầu vào là horsepower trong tập train
 lm.fit=lm(mpg∼horsepower ,data=Auto)
# Tính các hệ số B0 và B1 của mô hình
coef(lm.fit)
(Intercept)  horsepower 
 39.9358610  -0.1578447 
# Load thư viện boot
library(boot)
# Fit mô hình hồi quy tuyến tính để dự đoán biến đầu ra mpg khi biến đầu vào là horsepower trong tập train
glm.fit=glm(mpg∼horsepower ,data=Auto)
# Hàm cv.glm() dùng để tính sai số K-fold cross-validation cho mô hình tuyến tính
cv.err=cv.glm(Auto ,glm.fit)
# Hiển thị 2 sai số, sai số đầu là sai số cross-validation, sai số thứ 2 là sai số hiệu chỉnh khi đền bù cho thiên vị (bias)
cv.err$delta
[1] 24.23151 24.23114
# Tạo ra 1 vector có 5 giá trị = 0
cv.error=rep(0,5)
# Hiển thị các sai số cross-validation của 5 mô hình từ bậc 1 -> 5
cv.error
[1] 24.23151 19.24821 19.33498 19.42443 19.03321

Không có sự cải thiện thực sự đáng kể khi tăng bậc của mô hình.

3) k-Fold Cross-Validation

Chúng ta sẽ fit từng mô hình từ bậc 1 cho tới bậc 10 và lưu trữ giá trị sai số cross-validaiton trong vector cv.error.10

# 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(17)
# Tạo ra 1 vector có 10 giá trị = 0
cv.error.10=rep(0 ,10)
# Với phương pháp k-FOld CV này, chúng ta thử đặt k=10 tức tập dữ liệu sẽ chia làm 10, 9 phục vụ cho tập train, và 1 cho tập validation, test error sẽ được tính bằng cách lấy trung bình của 10 MSE.
for (i in 1:10) {
glm.fit=glm(mpg~poly(horsepower,i),data=Auto)
cv.error.10[i]=cv.glm(Auto,glm.fit,K=10)$delta[1]
}
# Hiển thị các sai số cross-validation của 10 mô hình từ bậc 1 -> 10
cv.error.10
 [1] 24.27207 19.26909 19.34805 19.29496 19.03198 18.89781 19.12061 19.14666 18.87013 20.95520

Khi dùng k-fold Cross-Validation thì tốc độ tính toán nhanh hơn nhiều so với LOOCV, và ở đây chúng ta Không có sự cải thiện thực sự đáng kể khi tăng bậc của mô hình khi sai số dao động tăng giảm liên tục.

4) The Bootstrap

Tính toán độ chính xác thống kê

# Tạo ra 1 hàm để tính toán độ chính xác thống kê (α) khi được truyền vào data và các quan sát được sử dụng cho việc tính toán α
alpha.fn=function(data,index){
X=data$X[index]
Y=data$Y[index]
return((var(Y)-cov(X,Y))/(var(X) + var(Y)-2*cov(X,Y)))
}
# Tính toán α sử dụng 100 quan sát đầu trong dữ liệu Portfolio
alpha.fn(Portfolio ,1:100)
[1] 0.5758321
# 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)
# Sử dụng hàm sample() để chọn 100 quan sát quan sát theo thứ tự ngẫu nhiên từ 100 quan sát ban đầu để tính toán α
alpha.fn(Portfolio,sample(100,100,replace=T))
[1] 0.7368375

Sử dụng hàm boot() để phân tích bootstrap

# Tạo ra 1000 boostrap để tính toán α
boot(Portfolio ,alpha.fn,R=1000)

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Portfolio, statistic = alpha.fn, R = 1000)


Bootstrap Statistics :
     original       bias    std. error
t1* 0.5758321 -0.001695873  0.09366347

Hàm cho chúng ta thông tin về α = 0.5758 cho dữ liệu ban đầu, mức thiên vị (bias) và sai số chuẩn bootstrap SE(ˆα) = 0.09366.

Tính toán độ chính xác mô hình hồi quy

# Tạo ra hàm để tính 2 hệ số B0 và B1 của mô hình hồi quy tuyến tính dự đoán giá trị đầu ra là mpg và đầu vào là horsepower với số lượng quan sát tương ứng với index được truyền vào
boot.fn=function(data,index)
return(coef(lm(mpg~horsepower,data=data,subset=index)))
# Tính 2 hệ số B0 và B1 của mô hình hồi quy tuyến tính dự đoán giá trị đầu ra là mpg và đầu vào là horsepower với 392 quan sát đầu trong dữ liệu Auto
boot.fn(Auto ,1:392)
(Intercept)  horsepower 
 39.9358610  -0.1578447 
# 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)
# Sử dụng hàm sample() để chọn 392 quan sát quan sát theo thứ tự ngẫu nhiên từ 392 quan sát ban đầu để tính toán B0 và B1
boot.fn(Auto,sample(392,392,replace=T))
(Intercept)  horsepower 
 40.3404517  -0.1634868 
# Sử dụng hàm sample() để chọn 392 quan sát quan sát theo thứ tự ngẫu nhiên từ 392 quan sát ban đầu để tính toán B0 và B1
boot.fn(Auto,sample(392,392,replace=T))
(Intercept)  horsepower 
 40.1186906  -0.1577063 
# Sử dụng hàm boot() để tính toán sai số chuẩn của 1000 bootstrap cho 2 hệ số B0 và B1
boot(Auto,boot.fn,1000)

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Auto, statistic = boot.fn, R = 1000)


Bootstrap Statistics :
      original        bias    std. error
t1* 39.9358610  0.0544513229 0.841289790
t2* -0.1578447 -0.0006170901 0.007343073

Chúng ta thấy rằng sai số chuẩn của B0 và B1 lần lượt là : SE(βˆ0) = 0.84129, SE(βˆ1) is 0.00734.

# Phân tich các hệ số của mô hình tuyến tính ở trên
summary(lm(mpg~horsepower,data=Auto))$coef
              Estimate  Std. Error   t value      Pr(>|t|)
(Intercept) 39.9358610 0.717498656  55.65984 1.220362e-187
horsepower  -0.1578447 0.006445501 -24.48914  7.031989e-81

Giá trị dự đoán mpg = 39.9358610 - 0.1578447 x horsepower, p-value của horsepower tương đối lớn nên không có bằng chứng thống kê cho thấy có mối quan hệ giữa biến horsepower và mpg.

# Tạo hàm tính sai số chuẩn boostrap của mô hình bậc 2 dự đoán giá trị đầu ra là mpg và đầu vào là horsepower với số lượng quan sát tương ứng với index được truyền vào
boot.fn=function (data ,index)
coefficients(lm(mpg∼horsepower +I(horsepower ^2),data=data ,
subset=index))
# 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)
# Sử dụng hàm boot() để tính toán sai số chuẩn của 1000 bootstrap cho 3 hệ số B0,B1, B3 
boot(Auto,boot.fn ,1000)

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Auto, statistic = boot.fn, R = 1000)


Bootstrap Statistics :
        original        bias     std. error
t1* 56.900099702  3.511640e-02 2.0300222526
t2* -0.466189630 -7.080834e-04 0.0324241984
t3*  0.001230536  2.840324e-06 0.0001172164

Chúng ta thấy rằng sai số chuẩn boostrap của B0, B1, B3 lần lượt là : SE(βˆ0) = 2.0300222526, SE(βˆ1) = 0.0324241984, SE(βˆ2) = 0.0001172164.

LS0tDQp0aXRsZTogIlRo4buxYyBow6BuaCBjb2RlXyBUdeG6p24gNiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyBUw6puOiBNYWkgSHV5DQojIyMgTVNTVjogNDMuMDEuMTA0LjA2NQ0KIyMjIFPhu5EgdGjhu6kgdOG7sTogMDgNCg0KDQojIyAxKSBUaGUgVmFsaWRhdGlvbiBTZXQgQXBwcm9hZA0KDQoNCg0KYGBge3J9DQojIExvYWQgdGjGsCB2aeG7h24gSVNMUg0KbGlicmFyeShJU0xSKQ0KYGBgDQoNCg0KYGBge3J9DQojIHNldC5zZWVkIGTDuW5nIMSR4buDIHTDoWkgdOG6oW8gbmjhu69uZyB2ZWN0b3IgcmFuZG9tIGdp4buRbmcgbmhhdSB0aGVvIHTGsMahbmcg4bupbmcgduG7m2kgZ2nDoSB0cuG7iyDEkcaw4bujYyDEkcawYSB2w6BvIGjDoG0gc2VlZA0Kc2V0LnNlZWQgKDEpDQojIFThuqFvIDEgdOG6rXAgdHJhaW4gY8OzIDE5NiBxdWFuIHPDoXQgxJHGsOG7o2MgcsO6dCB0csOtY2ggbmfhuqt1IG5oacOqbiB04burIDM5MiBxdWFuIHPDoXQgYmFuIMSR4bqndQ0KdHJhaW4gPSBzYW1wbGUoMzkyICwxOTYpDQpgYGANCg0KDQpgYGB7cn0NCiMgRml0IG3DtCBow6xuaCBo4buTaSBxdXkgxJHGoW4gZ2nhuqNuIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIHRyb25nIHThuq1wIHRyYWluDQpsbS5maXQ9bG0obXBnfmhvcnNlcG93ZXIsZGF0YT1BdXRvLHN1YnNldD10cmFpbikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBhdHRhY2ggZMO5bmcgxJHhu4Mga2hp4bq/biBjaG8gbmjhu69uZyBiaeG6v24gZmVhdHVyZSB0cm9uZyBk4buvIGxp4buHdSBjw7Mgc+G6tW4gdHJvbmcgUnN0dWRpbyB0aGVvIHTDqm4NCmF0dGFjaChBdXRvKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgU+G7rSBk4bulbmcgaMOgbSBwcmVkaWN0IMSR4buDIMaw4bubYyBsxrDhu6NuZyBnacOhIHRy4buLIG91dHB1dCBjaG8gMzkyIHF1YW4gc8OhdCB2w6Agc+G7rSBk4bulbmcgaMOgbSBtZWFuKCkgxJHhu4MgdMOtbmggTVNFIGPhu6dhIDE5NiBxdWFuIHPDoXQgdHJvbmcgdOG6rXAgdmFsaWRhdGlvbg0KbWVhbigobXBnIC1wcmVkaWN0IChsbS5maXQgLEF1dG8pKVstdHJhaW4gXV4yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBi4bqtYyAyIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIHRyb25nIHThuq1wIHRyYWluDQpsbS5maXQyPWxtKG1wZ+KIvHBvbHkoaG9yc2Vwb3dlciAsMiksZGF0YT1BdXRvICwgc3Vic2V0PXRyYWluKQ0KYGBgDQoNCg0KYGBge3J9DQojIFTDrW5oIGdpw6EgdHLhu4sgTVNFIGNobyB04bqtcCB2YWxpZGF0aW9uIGfhu5NtIDE5NiBxdWFuIHPDoXQNCm1lYW4oKG1wZyAtcHJlZGljdCAobG0uZml0MiAsQXV0byApKVstIHRyYWluXV4yKQ0KYGBgDQoNCg0KYGBge3J9DQojIEZpdCBtw7QgaMOsbmggaOG7k2kgcXV5IGLhuq1jIDMgxJHhu4MgZOG7sSDEkW/DoW4gYmnhur9uIMSR4bqndSByYSBtcGcgdHJvbmcgdOG6rXAgdHJhaW4NCmxtLmZpdDM9bG0obXBn4oi8cG9seShob3JzZXBvd2VyICwzKSxkYXRhPUF1dG8gLCBzdWJzZXQ9dHJhaW4pDQpgYGANCg0KYGBge3J9DQojIFTDrW5oIGdpw6EgdHLhu4sgTVNFIGNobyB04bqtcCB2YWxpZGF0aW9uIGfhu5NtIDE5NiBxdWFuIHPDoXQNCm1lYW4oKG1wZyAtcHJlZGljdCAobG0uZml0MyAsQXV0byApKVstIHRyYWluXV4yKQ0KYGBgDQoNClZhbGlkYXRpb24gc2V0IGVycm9yIGNobyBtw7QgaMOsbmggaMOgbSBi4bqtYyAyIHbDoCAzIGzhuqduIGzGsOG7o3QgbMOgIDE4LjcxIHbDoCAxOC43OS4gVHV5IG5oacOqbiBraGkgY2jDum5nIHRhIGPDsyB04bqtcCB0cmFpbmluZyBuZ+G6q3Ugbmhpw6puIGtow6FjLCBuaOG7r25nIGNvbiBz4buRIG7DoHkgY8Wpbmcgc+G6vSB0aGF5IMSR4buVaQ0KDQoNCg0KDQpgYGB7cn0NCiMgc2V0LnNlZWQgZMO5bmcgxJHhu4MgdMOhaSB04bqhbyBuaOG7r25nIHZlY3RvciByYW5kb20gZ2nhu5FuZyBuaGF1IHRoZW8gdMawxqFuZyDhu6luZyB24bubaSBnacOhIHRy4buLIMSRxrDhu6NjIMSRxrBhIHbDoG8gaMOgbSBzZWVkDQpzZXQuc2VlZCgyKQ0KIyBU4bqhbyAxIHThuq1wIHRyYWluIGPDsyAxOTYgcXVhbiBzw6F0IMSRxrDhu6NjIHLDunQgdHLDrWNoIG5n4bqrdSBuaGnDqm4gdOG7qyAzOTIgcXVhbiBzw6F0IGJhbiDEkeG6p3UNCnRyYWluID0gc2FtcGxlKDM5MiAsMTk2KQ0KYGBgDQoNCmBgYHtyfQ0KIyBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBi4bqtYyAxIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIHRyb25nIHThuq1wIHRyYWluDQpsbS5maXQ9bG0obXBn4oi8aG9yc2Vwb3dlciAsc3Vic2V0PXRyYWluKQ0KYGBgDQoNCg0KYGBge3J9DQojIFTDrW5oIGdpw6EgdHLhu4sgTVNFIGNobyB04bqtcCB2YWxpZGF0aW9uIGfhu5NtIDE5NiBxdWFuIHPDoXQNCm1lYW4oKG1wZyAtcHJlZGljdCAobG0uZml0ICxBdXRvKSlbLXRyYWluIF1eMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBi4bqtYyAyIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIHRyb25nIHThuq1wIHRyYWluDQpsbS5maXQyPWxtKG1wZ+KIvHBvbHkoaG9yc2Vwb3dlciAsMiksZGF0YT1BdXRvICwgc3Vic2V0PXRyYWluKQ0KYGBgDQoNCg0KYGBge3J9DQojIFTDrW5oIGdpw6EgdHLhu4sgTVNFIGNobyB04bqtcCB2YWxpZGF0aW9uIGfhu5NtIDE5NiBxdWFuIHPDoXQNCm1lYW4oKG1wZyAtcHJlZGljdCAobG0uZml0MiAsQXV0byApKVstIHRyYWluXV4yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBi4bqtYyAzIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIHRyb25nIHThuq1wIHRyYWluDQogbG0uZml0Mz1sbShtcGfiiLxwb2x5KGhvcnNlcG93ZXIgLDMpLGRhdGE9QXV0byAsIHN1YnNldD10cmFpbikNCg0KYGBgDQoNCg0KYGBge3J9DQojIFTDrW5oIGdpw6EgdHLhu4sgTVNFIGNobyB04bqtcCB2YWxpZGF0aW9uIGfhu5NtIDE5NiBxdWFuIHPDoXQNCm1lYW4oKG1wZyAtcHJlZGljdCAobG0uZml0MyAsQXV0byApKVstIHRyYWluXV4yKQ0KYGBgDQoNCiBWYWxpZGF0aW9uIHNldCBlcnJvciBjaG8gbcO0IGjDrG5oIGjhu5NpIHF1eSBi4bqtYyAxLDIsMyBs4bqnbiBsxrDhu6N0IGzDoCAyNSw3MiwgMjAuNDMsIDIwLjM4LiBDaMO6bmcgdGEgdGjhuqV5IG3DtCBow6xuaCBi4bqtYyAyIGPhuqNpIHRoaeG7h24gc2FpIHPhu5EgcuG6pXQgbmhp4buBdSBzbyB24bubaSBi4bqtYyAxIGPDsm4gbcO0IGjDrG5oIGLhuq1jIDMgdGjDrCBzYWkgc+G7kSBj4bqjaSB0aGnhu4duIGtow7RuZyBxdcOhIMSRw6FuZyBr4buDLg0KIA0KIA0KIyMgMikgTGVhdmUtT25lLU91dCBDcm9zcy1WYWxpZGF0aW9uDQogDQogDQpgYGB7cn0NCiMgRml0IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCDEkeG7gyBk4buxIMSRb8OhbiBiaeG6v24gxJHhuqd1IHJhIG1wZyBraGkgYmnhur9uIMSR4bqndSB2w6BvIGzDoCBob3JzZXBvd2VyIHRyb25nIHThuq1wIHRyYWluDQpnbG0uZml0ID0gZ2xtKG1wZ35ob3JzZXBvd2VyICxkYXRhPSBBdXRvKQ0KYGBgDQogDQpgYGB7cn0NCiMgVMOtbmggY8OhYyBo4buHIHPhu5EgQjAgdsOgIEIxIGPhu6dhIG3DtCBow6xuaA0KY29lZihnbG0uZml0KQ0KYGBgDQoNCk5nb8OgaSBjw6FjaCB2aeG6v3QgdHLDqm4gY2jDum5nIHRhIGPDsyB0aOG7gyBi4bqxbmcgaMOgbSBsbSgpIHF1ZW4gdGh14buZYw0KDQpgYGB7cn0NCiMgRml0IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCDEkeG7gyBk4buxIMSRb8OhbiBiaeG6v24gxJHhuqd1IHJhIG1wZyBraGkgYmnhur9uIMSR4bqndSB2w6BvIGzDoCBob3JzZXBvd2VyIHRyb25nIHThuq1wIHRyYWluDQogbG0uZml0PWxtKG1wZ+KIvGhvcnNlcG93ZXIgLGRhdGE9QXV0bykNCmBgYA0KDQpgYGB7cn0NCiMgVMOtbmggY8OhYyBo4buHIHPhu5EgQjAgdsOgIEIxIGPhu6dhIG3DtCBow6xuaA0KY29lZihsbS5maXQpDQpgYGANCg0KIA0KYGBge3J9DQojIExvYWQgdGjGsCB2aeG7h24gYm9vdA0KbGlicmFyeShib290KQ0KIyBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIMSR4buDIGThu7EgxJFvw6FuIGJp4bq/biDEkeG6p3UgcmEgbXBnIGtoaSBiaeG6v24gxJHhuqd1IHbDoG8gbMOgIGhvcnNlcG93ZXIgdHJvbmcgdOG6rXAgdHJhaW4NCmdsbS5maXQ9Z2xtKG1wZ+KIvGhvcnNlcG93ZXIgLGRhdGE9QXV0bykNCmBgYA0KDQoNCg0KYGBge3J9DQojIEjDoG0gY3YuZ2xtKCkgZMO5bmcgxJHhu4MgdMOtbmggc2FpIHPhu5EgSy1mb2xkIGNyb3NzLXZhbGlkYXRpb24gY2hvIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oDQpjdi5lcnI9Y3YuZ2xtKEF1dG8gLGdsbS5maXQpDQpgYGANCg0KYGBge3J9DQojIEhp4buDbiB0aOG7iyAyIHNhaSBz4buRLCBzYWkgc+G7kSDEkeG6p3UgbMOgIHNhaSBz4buRIGNyb3NzLXZhbGlkYXRpb24sIHNhaSBz4buRIHRo4bupIDIgbMOgIHNhaSBz4buRIGhp4buHdSBjaOG7iW5oIGtoaSDEkeG7gW4gYsO5IGNobyB0aGnDqm4gduG7iyAoYmlhcykNCmN2LmVyciRkZWx0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBU4bqhbyByYSAxIHZlY3RvciBjw7MgNSBnacOhIHRy4buLID0gMA0KY3YuZXJyb3I9cmVwKDAsNSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2jDum5nIHRhIHPhur0gZml0IHThu6tuZyBtw7QgaMOsbmggdOG7qyBi4bqtYyAxIGNobyB04bubaSBi4bqtYyA1IHbDoCBsxrB1IHRy4buvIGdpw6EgdHLhu4sgc2FpIHPhu5EgY3Jvc3MtdmFsaWRhaXRvbiB0cm9uZyB2ZWN0b3IgY3YuZXJyb3INCmZvciAoaSBpbiAxOjUpew0KZ2xtLmZpdD1nbG0obXBnfnBvbHkoaG9yc2Vwb3dlcixpKSxkYXRhPUF1dG8pDQpjdi5lcnJvcltpXSA9IGN2LmdsbShBdXRvLGdsbS5maXQpJGRlbHRhIFsxXQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQojIEhp4buDbiB0aOG7iyBjw6FjIHNhaSBz4buRIGNyb3NzLXZhbGlkYXRpb24gY+G7p2EgNSBtw7QgaMOsbmggdOG7qyBi4bqtYyAxIC0+IDUNCmN2LmVycm9yDQpgYGANCg0KS2jDtG5nIGPDsyBz4buxIGPhuqNpIHRoaeG7h24gdGjhu7FjIHPhu7EgxJHDoW5nIGvhu4Mga2hpIHTEg25nIGLhuq1jIGPhu6dhIG3DtCBow6xuaC4gDQoNCg0KIyMgMykgay1Gb2xkIENyb3NzLVZhbGlkYXRpb24NCg0KQ2jDum5nIHRhIHPhur0gZml0IHThu6tuZyBtw7QgaMOsbmggdOG7qyBi4bqtYyAxIGNobyB04bubaSBi4bqtYyAxMCB2w6AgbMawdSB0cuG7ryBnacOhIHRy4buLIHNhaSBz4buRIGNyb3NzLXZhbGlkYWl0b24gdHJvbmcgdmVjdG9yIGN2LmVycm9yLjEwDQoNCmBgYHtyfQ0KIyBzZXQuc2VlZCBkw7luZyDEkeG7gyB0w6FpIHThuqFvIG5o4buvbmcgdmVjdG9yIHJhbmRvbSBnaeG7kW5nIG5oYXUgdGhlbyB0xrDGoW5nIOG7qW5nIHbhu5tpIGdpw6EgdHLhu4sgxJHGsOG7o2MgxJHGsGEgdsOgbyBow6BtIHNlZWQNCnNldC5zZWVkKDE3KQ0KIyBU4bqhbyByYSAxIHZlY3RvciBjw7MgMTAgZ2nDoSB0cuG7iyA9IDANCmN2LmVycm9yLjEwPXJlcCgwICwxMCkNCiMgVuG7m2kgcGjGsMahbmcgcGjDoXAgay1GT2xkIENWIG7DoHksIGNow7puZyB0YSB0aOG7rSDEkeG6t3Qgaz0xMCB04bupYyB04bqtcCBk4buvIGxp4buHdSBz4bq9IGNoaWEgbMOgbSAxMCwgOSBwaOG7pWMgduG7pSBjaG8gdOG6rXAgdHJhaW4sIHbDoCAxIGNobyB04bqtcCB2YWxpZGF0aW9uLCB0ZXN0IGVycm9yIHPhur0gxJHGsOG7o2MgdMOtbmggYuG6sW5nIGPDoWNoIGzhuqV5IHRydW5nIGLDrG5oIGPhu6dhIDEwIE1TRS4NCmZvciAoaSBpbiAxOjEwKSB7DQpnbG0uZml0PWdsbShtcGd+cG9seShob3JzZXBvd2VyLGkpLGRhdGE9QXV0bykNCmN2LmVycm9yLjEwW2ldPWN2LmdsbShBdXRvLGdsbS5maXQsSz0xMCkkZGVsdGFbMV0NCn0NCmBgYA0KDQpgYGB7cn0NCiMgSGnhu4NuIHRo4buLIGPDoWMgc2FpIHPhu5EgY3Jvc3MtdmFsaWRhdGlvbiBj4bunYSAxMCBtw7QgaMOsbmggdOG7qyBi4bqtYyAxIC0+IDEwDQpjdi5lcnJvci4xMA0KYGBgDQoNCktoaSBkw7luZyBrLWZvbGQgQ3Jvc3MtVmFsaWRhdGlvbiB0aMOsIHThu5FjIMSR4buZIHTDrW5oIHRvw6FuIG5oYW5oIGjGoW4gbmhp4buBdSBzbyB24bubaSBMT09DViwgdsOgIOG7nyDEkcOieSBjaMO6bmcgdGEgS2jDtG5nIGPDsyBz4buxIGPhuqNpIHRoaeG7h24gdGjhu7FjIHPhu7EgxJHDoW5nIGvhu4Mga2hpIHTEg25nIGLhuq1jIGPhu6dhIG3DtCBow6xuaCBraGkgc2FpIHPhu5EgZGFvIMSR4buZbmcgdMSDbmcgZ2nhuqNtIGxpw6puIHThu6VjLg0KDQoNCiMjIDQpIFRoZSBCb290c3RyYXANCg0KIyMjIFTDrW5oIHRvw6FuIMSR4buZIGNow61uaCB4w6FjIHRo4buRbmcga8OqDQoNCmBgYHtyfQ0KIyBU4bqhbyByYSAxIGjDoG0gxJHhu4MgdMOtbmggdG/DoW4gxJHhu5kgY2jDrW5oIHjDoWMgdGjhu5FuZyBrw6ogKM6xKSBraGkgxJHGsOG7o2MgdHJ1eeG7gW4gdsOgbyBkYXRhIHbDoCBjw6FjIHF1YW4gc8OhdCDEkcaw4bujYyBz4butIGThu6VuZyBjaG8gdmnhu4djIHTDrW5oIHRvw6FuIM6xDQphbHBoYS5mbj1mdW5jdGlvbihkYXRhLGluZGV4KXsNClg9ZGF0YSRYW2luZGV4XQ0KWT1kYXRhJFlbaW5kZXhdDQpyZXR1cm4oKHZhcihZKS1jb3YoWCxZKSkvKHZhcihYKSArIHZhcihZKS0yKmNvdihYLFkpKSkNCn0NCmBgYA0KDQpgYGB7cn0NCiMgVMOtbmggdG/DoW4gzrEgc+G7rSBk4bulbmcgMTAwIHF1YW4gc8OhdCDEkeG6p3UgdHJvbmcgZOG7ryBsaeG7h3UgUG9ydGZvbGlvDQphbHBoYS5mbihQb3J0Zm9saW8gLDE6MTAwKQ0KYGBgDQoNCmBgYHtyfQ0KIyBzZXQuc2VlZCBkw7luZyDEkeG7gyB0w6FpIHThuqFvIG5o4buvbmcgdmVjdG9yIHJhbmRvbSBnaeG7kW5nIG5oYXUgdGhlbyB0xrDGoW5nIOG7qW5nIHbhu5tpIGdpw6EgdHLhu4sgxJHGsOG7o2MgxJHGsGEgdsOgbyBow6BtIHNlZWQNCnNldC5zZWVkICgxKQ0KIyBT4butIGThu6VuZyBow6BtIHNhbXBsZSgpIMSR4buDIGNo4buNbiAxMDAgcXVhbiBzw6F0IHF1YW4gc8OhdCB0aGVvIHRo4bupIHThu7Egbmfhuqt1IG5oacOqbiB04burIDEwMCBxdWFuIHPDoXQgYmFuIMSR4bqndSDEkeG7gyB0w61uaCB0b8OhbiDOsQ0KYWxwaGEuZm4oUG9ydGZvbGlvLHNhbXBsZSgxMDAsMTAwLHJlcGxhY2U9VCkpDQpgYGANCg0KU+G7rSBk4bulbmcgaMOgbSBib290KCkgxJHhu4MgcGjDom4gdMOtY2ggYm9vdHN0cmFwDQoNCmBgYHtyfQ0KIyBU4bqhbyByYSAxMDAwIGJvb3N0cmFwIMSR4buDIHTDrW5oIHRvw6FuIM6xDQpib290KFBvcnRmb2xpbyAsYWxwaGEuZm4sUj0xMDAwKQ0KYGBgDQoNCkjDoG0gY2hvIGNow7puZyB0YSB0aMO0bmcgdGluIHbhu4EgzrEgPSAwLjU3NTggY2hvIGThu68gbGnhu4d1IGJhbiDEkeG6p3UsIG3hu6ljIHRoacOqbiB24buLIChiaWFzKSB2w6Agc2FpIHPhu5EgY2h14bqpbiBib290c3RyYXAgU0Uoy4bOsSkgPSAwLjA5MzY2Lg0KDQojIyMgVMOtbmggdG/DoW4gxJHhu5kgY2jDrW5oIHjDoWMgbcO0IGjDrG5oIGjhu5NpIHF1eQ0KDQpgYGB7cn0NCiMgVOG6oW8gcmEgaMOgbSDEkeG7gyB0w61uaCAyIGjhu4cgc+G7kSBCMCB2w6AgQjEgY+G7p2EgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIGThu7EgxJFvw6FuIGdpw6EgdHLhu4sgxJHhuqd1IHJhIGzDoCBtcGcgdsOgIMSR4bqndSB2w6BvIGzDoCBob3JzZXBvd2VyIHbhu5tpIHPhu5EgbMaw4bujbmcgcXVhbiBzw6F0IHTGsMahbmcg4bupbmcgduG7m2kgaW5kZXggxJHGsOG7o2MgdHJ1eeG7gW4gdsOgbw0KYm9vdC5mbj1mdW5jdGlvbihkYXRhLGluZGV4KQ0KcmV0dXJuKGNvZWYobG0obXBnfmhvcnNlcG93ZXIsZGF0YT1kYXRhLHN1YnNldD1pbmRleCkpKQ0KDQpgYGANCg0KYGBge3J9DQojIFTDrW5oIDIgaOG7hyBz4buRIEIwIHbDoCBCMSBj4bunYSBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggZOG7sSDEkW/DoW4gZ2nDoSB0cuG7iyDEkeG6p3UgcmEgbMOgIG1wZyB2w6AgxJHhuqd1IHbDoG8gbMOgIGhvcnNlcG93ZXIgduG7m2kgMzkyIHF1YW4gc8OhdCDEkeG6p3UgdHJvbmcgZOG7ryBsaeG7h3UgQXV0bw0KYm9vdC5mbihBdXRvICwxOjM5MikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBzZXQuc2VlZCBkw7luZyDEkeG7gyB0w6FpIHThuqFvIG5o4buvbmcgdmVjdG9yIHJhbmRvbSBnaeG7kW5nIG5oYXUgdGhlbyB0xrDGoW5nIOG7qW5nIHbhu5tpIGdpw6EgdHLhu4sgxJHGsOG7o2MgxJHGsGEgdsOgbyBow6BtIHNlZWQNCnNldC5zZWVkICgxKQ0KIyBT4butIGThu6VuZyBow6BtIHNhbXBsZSgpIMSR4buDIGNo4buNbiAzOTIgcXVhbiBzw6F0IHF1YW4gc8OhdCB0aGVvIHRo4bupIHThu7Egbmfhuqt1IG5oacOqbiB04burIDM5MiBxdWFuIHPDoXQgYmFuIMSR4bqndSDEkeG7gyB0w61uaCB0b8OhbiBCMCB2w6AgQjENCmJvb3QuZm4oQXV0byxzYW1wbGUoMzkyLDM5MixyZXBsYWNlPVQpKQ0KIyBT4butIGThu6VuZyBow6BtIHNhbXBsZSgpIMSR4buDIGNo4buNbiAzOTIgcXVhbiBzw6F0IHF1YW4gc8OhdCB0aGVvIHRo4bupIHThu7Egbmfhuqt1IG5oacOqbiB04burIDM5MiBxdWFuIHPDoXQgYmFuIMSR4bqndSDEkeG7gyB0w61uaCB0b8OhbiBCMCB2w6AgQjENCmJvb3QuZm4oQXV0byxzYW1wbGUoMzkyLDM5MixyZXBsYWNlPVQpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBT4butIGThu6VuZyBow6BtIGJvb3QoKSDEkeG7gyB0w61uaCB0b8OhbiBzYWkgc+G7kSBjaHXhuqluIGPhu6dhIDEwMDAgYm9vdHN0cmFwIGNobyAyIGjhu4cgc+G7kSBCMCB2w6AgQjENCmJvb3QoQXV0byxib290LmZuLDEwMDApDQpgYGANCg0KQ2jDum5nIHRhIHRo4bqleSBy4bqxbmcgc2FpIHPhu5EgY2h14bqpbiBj4bunYSBCMCB2w6AgQjEgbOG6p24gbMaw4bujdCBsw6AgOiBTRSjOssuGMCkgPSAwLjg0MTI5LCBTRSjOssuGMSkgaXMgMC4wMDczNC4NCg0KYGBge3J9DQojIFBow6JuIHRpY2ggY8OhYyBo4buHIHPhu5EgY+G7p2EgbcO0IGjDrG5oIHR1eeG6v24gdMOtbmgg4bufIHRyw6puDQpzdW1tYXJ5KGxtKG1wZ35ob3JzZXBvd2VyLGRhdGE9QXV0bykpJGNvZWYNCmBgYA0KDQpHacOhIHRy4buLIGThu7EgxJFvw6FuIG1wZyA9IDM5LjkzNTg2MTAgLSAwLjE1Nzg0NDcgeCBob3JzZXBvd2VyLCBwLXZhbHVlIGPhu6dhIGhvcnNlcG93ZXIgdMawxqFuZyDEkeG7kWkgbOG7m24gbsOqbiBraMO0bmcgY8OzIGLhurFuZyBjaOG7qW5nIHRo4buRbmcga8OqIGNobyB0aOG6pXkgY8OzIG3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgYmnhur9uIGhvcnNlcG93ZXIgdsOgIG1wZy4NCg0KYGBge3J9DQojIFThuqFvIGjDoG0gdMOtbmggc2FpIHPhu5EgY2h14bqpbiBib29zdHJhcCBj4bunYSBtw7QgaMOsbmggYuG6rWMgMiBk4buxIMSRb8OhbiBnacOhIHRy4buLIMSR4bqndSByYSBsw6AgbXBnIHbDoCDEkeG6p3UgdsOgbyBsw6AgaG9yc2Vwb3dlciB24bubaSBz4buRIGzGsOG7o25nIHF1YW4gc8OhdCB0xrDGoW5nIOG7qW5nIHbhu5tpIGluZGV4IMSRxrDhu6NjIHRydXnhu4FuIHbDoG8NCmJvb3QuZm49ZnVuY3Rpb24gKGRhdGEgLGluZGV4KQ0KY29lZmZpY2llbnRzKGxtKG1wZ+KIvGhvcnNlcG93ZXIgK0koaG9yc2Vwb3dlciBeMiksZGF0YT1kYXRhICwNCnN1YnNldD1pbmRleCkpDQpgYGANCg0KYGBge3J9DQojIHNldC5zZWVkIGTDuW5nIMSR4buDIHTDoWkgdOG6oW8gbmjhu69uZyB2ZWN0b3IgcmFuZG9tIGdp4buRbmcgbmhhdSB0aGVvIHTGsMahbmcg4bupbmcgduG7m2kgZ2nDoSB0cuG7iyDEkcaw4bujYyDEkcawYSB2w6BvIGjDoG0gc2VlZA0Kc2V0LnNlZWQoMSkNCiMgU+G7rSBk4bulbmcgaMOgbSBib290KCkgxJHhu4MgdMOtbmggdG/DoW4gc2FpIHPhu5EgY2h14bqpbiBj4bunYSAxMDAwIGJvb3RzdHJhcCBjaG8gMyBo4buHIHPhu5EgQjAsQjEsIEIzIA0KYm9vdChBdXRvLGJvb3QuZm4gLDEwMDApDQpgYGANCkNow7puZyB0YSB0aOG6pXkgcuG6sW5nIHNhaSBz4buRIGNodeG6qW4gYm9vc3RyYXAgY+G7p2EgQjAsIEIxLCBCMyBs4bqnbiBsxrDhu6N0IGzDoCA6IFNFKM6yy4YwKSA9IDIuMDMwMDIyMjUyNiwgU0UozrLLhjEpID0gMC4wMzI0MjQxOTg0LCBTRSjOssuGMikgPSAwLjAwMDExNzIxNjQuDQo=