1. KNN - WINE

Caret package

trainControl() - 훈련과정 중 parameter 설정 Ex) trainControl ( method = “repeatedcv” number = 10, <- 훈련데이터 fold 갯수 repeat - 5) <- cv 반복 횟수

expand.grid() - factor 조합의 데이터 프레임 생성 Ex) expand.grid (k=1:10)

train() Ex) train ( Class ~. , data = method= trContraol preProcess = c(“center”,“scale”), <- 표준화, tuneGrid = expand.grid(k=1:10), <- 파라미터값 목록 metric="Accuarcy) <- 모형방식

Accurarcy 정확도 = TP+TN/ Total

Kappa 통계량 = p0-pe/1-pe p0: 관측 정확도 / pe: 기대 정확도

## 'data.frame':    178 obs. of  14 variables:
##  $ Alcohol             : num  14.2 13.2 13.2 14.4 13.2 ...
##  $ Acid                : num  1.71 1.78 2.36 1.95 2.59 1.76 1.87 2.15 1.64 1.35 ...
##  $ Ash                 : num  2.43 2.14 2.67 2.5 2.87 2.45 2.45 2.61 2.17 2.27 ...
##  $ Alcalinity          : num  15.6 11.2 18.6 16.8 21 15.2 14.6 17.6 14 16 ...
##  $ Magnesium           : int  127 100 101 113 118 112 96 121 97 98 ...
##  $ Total_phenols       : num  2.8 2.65 2.8 3.85 2.8 3.27 2.5 2.6 2.8 2.98 ...
##  $ Flavanoids          : num  3.06 2.76 3.24 3.49 2.69 3.39 2.52 2.51 2.98 3.15 ...
##  $ Nonflavanoid_phenols: num  0.28 0.26 0.3 0.24 0.39 0.34 0.3 0.31 0.29 0.22 ...
##  $ Proanthocyanins     : num  2.29 1.28 2.81 2.18 1.82 1.97 1.98 1.25 1.98 1.85 ...
##  $ color_intensity     : num  5.64 4.38 5.68 7.8 4.32 6.75 5.25 5.05 5.2 7.22 ...
##  $ Hue                 : num  1.04 1.05 1.03 0.86 1.04 1.05 1.02 1.06 1.08 1.01 ...
##  $ X0D280              : num  3.92 3.4 3.17 3.45 2.93 2.85 3.58 3.58 2.85 3.55 ...
##  $ proline             : int  1065 1050 1185 1480 735 1450 1290 1295 1045 1045 ...
##  $ Class               : Factor w/ 3 levels "1","2","3": 1 1 1 1 1 1 1 1 1 1 ...

Train, Test 분할

모형

## k-Nearest Neighbors 
## 
## 124 samples
##  13 predictor
##   3 classes: '1', '2', '3' 
## 
## Pre-processing: centered (13), scaled (13) 
## Resampling: Cross-Validated (10 fold, repeated 5 times) 
## Summary of sample sizes: 111, 112, 112, 111, 110, 112, ... 
## Resampling results across tuning parameters:
## 
##   k   Accuracy   Kappa    
##    1  0.9726041  0.9585595
##    2  0.9658725  0.9482692
##    3  0.9707859  0.9558296
##    4  0.9681901  0.9520425
##    5  0.9776956  0.9662325
##    6  0.9603280  0.9401272
##    7  0.9567100  0.9348356
##    8  0.9517965  0.9274056
##    9  0.9644023  0.9465077
##   10  0.9673876  0.9507825
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 5.

Prediction

## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3
##          1 14  2  0
##          2  0 24  0
##          3  0  1 13
## 
## Overall Statistics
##                                           
##                Accuracy : 0.9444          
##                  95% CI : (0.8461, 0.9884)
##     No Information Rate : 0.5             
##     P-Value [Acc > NIR] : 1.459e-12       
##                                           
##                   Kappa : 0.913           
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity            1.0000   0.8889   1.0000
## Specificity            0.9500   1.0000   0.9756
## Pos Pred Value         0.8750   1.0000   0.9286
## Neg Pred Value         1.0000   0.9000   1.0000
## Prevalence             0.2593   0.5000   0.2407
## Detection Rate         0.2593   0.4444   0.2407
## Detection Prevalence   0.2963   0.4444   0.2593
## Balanced Accuracy      0.9750   0.9444   0.9878

Importance Feature

2. Logistic - HeartAttack

## 'data.frame':    303 obs. of  14 variables:
##  $ age     : int  63 37 41 56 57 57 56 44 52 57 ...
##  $ sex     : int  1 1 0 1 0 1 0 1 1 1 ...
##  $ cp      : int  3 2 1 1 0 0 1 1 2 2 ...
##  $ trestbps: int  145 130 130 120 120 140 140 120 172 150 ...
##  $ chol    : int  233 250 204 236 354 192 294 263 199 168 ...
##  $ fbs     : int  1 0 0 0 0 0 0 0 1 0 ...
##  $ restecg : int  0 1 0 1 1 1 0 1 1 1 ...
##  $ thalach : int  150 187 172 178 163 148 153 173 162 174 ...
##  $ exang   : int  0 0 0 0 1 0 0 0 0 0 ...
##  $ oldpeak : num  2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
##  $ slope   : int  0 0 2 2 2 1 1 2 2 2 ...
##  $ ca      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ thal    : int  1 2 2 2 2 1 2 3 3 2 ...
##  $ target  : int  1 1 1 1 1 1 1 1 1 1 ...
## [1] 1 0
## Levels: 0 1

Tran, Test 나누기

Modelling

## Boosted Logistic Regression 
## 
## 212 samples
##  13 predictor
##   2 classes: '0', '1' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 212, 212, 212, 212, 212, 212, ... 
## Resampling results across tuning parameters:
## 
##   nIter  Accuracy   Kappa    
##   11     0.8065542  0.5981106
##   21     0.8094922  0.6078944
##   31     0.7919591  0.5711976
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was nIter = 21.

## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 33  7
##          1 15 36
##                                           
##                Accuracy : 0.7582          
##                  95% CI : (0.6572, 0.8419)
##     No Information Rate : 0.5275          
##     P-Value [Acc > NIR] : 4.977e-06       
##                                           
##                   Kappa : 0.5197          
##                                           
##  Mcnemar's Test P-Value : 0.1356          
##                                           
##             Sensitivity : 0.6875          
##             Specificity : 0.8372          
##          Pos Pred Value : 0.8250          
##          Neg Pred Value : 0.7059          
##              Prevalence : 0.5275          
##          Detection Rate : 0.3626          
##    Detection Prevalence : 0.4396          
##       Balanced Accuracy : 0.7624          
##                                           
##        'Positive' Class : 0               
## 

Importance Variables

3. Naive_bayes - WINE

## 'data.frame':    178 obs. of  14 variables:
##  $ Alcohol             : num  14.2 13.2 13.2 14.4 13.2 ...
##  $ Acid                : num  1.71 1.78 2.36 1.95 2.59 1.76 1.87 2.15 1.64 1.35 ...
##  $ Ash                 : num  2.43 2.14 2.67 2.5 2.87 2.45 2.45 2.61 2.17 2.27 ...
##  $ Alcalinity          : num  15.6 11.2 18.6 16.8 21 15.2 14.6 17.6 14 16 ...
##  $ Magnesium           : int  127 100 101 113 118 112 96 121 97 98 ...
##  $ Total_phenols       : num  2.8 2.65 2.8 3.85 2.8 3.27 2.5 2.6 2.8 2.98 ...
##  $ Flavanoids          : num  3.06 2.76 3.24 3.49 2.69 3.39 2.52 2.51 2.98 3.15 ...
##  $ Nonflavanoid_phenols: num  0.28 0.26 0.3 0.24 0.39 0.34 0.3 0.31 0.29 0.22 ...
##  $ Proanthocyanins     : num  2.29 1.28 2.81 2.18 1.82 1.97 1.98 1.25 1.98 1.85 ...
##  $ color_intensity     : num  5.64 4.38 5.68 7.8 4.32 6.75 5.25 5.05 5.2 7.22 ...
##  $ Hue                 : num  1.04 1.05 1.03 0.86 1.04 1.05 1.02 1.06 1.08 1.01 ...
##  $ X0D280              : num  3.92 3.4 3.17 3.45 2.93 2.85 3.58 3.58 2.85 3.55 ...
##  $ proline             : int  1065 1050 1185 1480 735 1450 1290 1295 1045 1045 ...
##  $ Class               : Factor w/ 3 levels "1","2","3": 1 1 1 1 1 1 1 1 1 1 ...

TRAIN/TEST 나누기

Modelling

## Naive Bayes 
## 
## 124 samples
##  13 predictor
##   3 classes: '1', '2', '3' 
## 
## Pre-processing: centered (13), scaled (13) 
## Resampling: Cross-Validated (10 fold, repeated 5 times) 
## Summary of sample sizes: 111, 112, 112, 111, 110, 112, ... 
## Resampling results across tuning parameters:
## 
##   usekernel  Accuracy   Kappa    
##   FALSE      0.9808142  0.9708775
##    TRUE      0.9756810  0.9631746
## 
## Tuning parameter 'laplace' was held constant at a value of 0
## Tuning
##  parameter 'adjust' was held constant at a value of 1
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were laplace = 0, usekernel = FALSE
##  and adjust = 1.

커널은 사용 x, 커널 o 에 따른 정확도를 보여줌

## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3
##          1 14  1  0
##          2  0 24  0
##          3  0  2 13
## 
## Overall Statistics
##                                           
##                Accuracy : 0.9444          
##                  95% CI : (0.8461, 0.9884)
##     No Information Rate : 0.5             
##     P-Value [Acc > NIR] : 1.459e-12       
##                                           
##                   Kappa : 0.913           
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity            1.0000   0.8889   1.0000
## Specificity            0.9750   1.0000   0.9512
## Pos Pred Value         0.9333   1.0000   0.8667
## Neg Pred Value         1.0000   0.9000   1.0000
## Prevalence             0.2593   0.5000   0.2407
## Detection Rate         0.2593   0.4444   0.2407
## Detection Prevalence   0.2778   0.4444   0.2778
## Balanced Accuracy      0.9875   0.9444   0.9756

ROC 커브 기준으로 변수 중요도를 선별해서 보여준다. ROC 커브 면적이 넓을 수록 중요도가 상승한다.

4. Descision Tree

기본 트리 설정

Cross-Validation

Pruning 가지치기

Prediciton

## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3
##          1 14  2  4
##          2  0 23  0
##          3  0  2  9
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8519          
##                  95% CI : (0.7288, 0.9338)
##     No Information Rate : 0.5             
##     P-Value [Acc > NIR] : 6.922e-08       
##                                           
##                   Kappa : 0.7692          
##                                           
##  Mcnemar's Test P-Value : 0.04601         
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity            1.0000   0.8519   0.6923
## Specificity            0.8500   1.0000   0.9512
## Pos Pred Value         0.7000   1.0000   0.8182
## Neg Pred Value         1.0000   0.8710   0.9070
## Prevalence             0.2593   0.5000   0.2407
## Detection Rate         0.2593   0.4259   0.1667
## Detection Prevalence   0.3704   0.4259   0.2037
## Balanced Accuracy      0.9250   0.9259   0.8218

5. Random Forest

## Random Forest 
## 
## 124 samples
##  13 predictor
##   3 classes: '1', '2', '3' 
## 
## Pre-processing: centered (13), scaled (13) 
## Resampling: Cross-Validated (10 fold, repeated 5 times) 
## Summary of sample sizes: 111, 110, 111, 112, 112, 111, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    2    0.9904529  0.9855947
##    7    0.9807326  0.9708665
##   13    0.9594539  0.9385166
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 2.

6. SVM Linear

## 'data.frame':    178 obs. of  14 variables:
##  $ Alcohol             : num  14.2 13.2 13.2 14.4 13.2 ...
##  $ Acid                : num  1.71 1.78 2.36 1.95 2.59 1.76 1.87 2.15 1.64 1.35 ...
##  $ Ash                 : num  2.43 2.14 2.67 2.5 2.87 2.45 2.45 2.61 2.17 2.27 ...
##  $ Alcalinity          : num  15.6 11.2 18.6 16.8 21 15.2 14.6 17.6 14 16 ...
##  $ Magnesium           : int  127 100 101 113 118 112 96 121 97 98 ...
##  $ Total_phenols       : num  2.8 2.65 2.8 3.85 2.8 3.27 2.5 2.6 2.8 2.98 ...
##  $ Flavanoids          : num  3.06 2.76 3.24 3.49 2.69 3.39 2.52 2.51 2.98 3.15 ...
##  $ Nonflavanoid_phenols: num  0.28 0.26 0.3 0.24 0.39 0.34 0.3 0.31 0.29 0.22 ...
##  $ Proanthocyanins     : num  2.29 1.28 2.81 2.18 1.82 1.97 1.98 1.25 1.98 1.85 ...
##  $ color_intensity     : num  5.64 4.38 5.68 7.8 4.32 6.75 5.25 5.05 5.2 7.22 ...
##  $ Hue                 : num  1.04 1.05 1.03 0.86 1.04 1.05 1.02 1.06 1.08 1.01 ...
##  $ X0D280              : num  3.92 3.4 3.17 3.45 2.93 2.85 3.58 3.58 2.85 3.55 ...
##  $ proline             : int  1065 1050 1185 1480 735 1450 1290 1295 1045 1045 ...
##  $ Class               : Factor w/ 3 levels "1","2","3": 1 1 1 1 1 1 1 1 1 1 ...
## Support Vector Machines with Linear Kernel 
## 
## 124 samples
##  13 predictor
##   3 classes: '1', '2', '3' 
## 
## Pre-processing: centered (13), scaled (13) 
## Resampling: Cross-Validated (10 fold, repeated 5 times) 
## Summary of sample sizes: 111, 111, 112, 113, 111, 112, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9474242  0.9207634
## 
## Tuning parameter 'C' was held constant at a value of 1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3
##          1 23  0  0
##          2  0 20  0
##          3  0  1 10
## 
## Overall Statistics
##                                           
##                Accuracy : 0.9815          
##                  95% CI : (0.9011, 0.9995)
##     No Information Rate : 0.4259          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.9709          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity            1.0000   0.9524   1.0000
## Specificity            1.0000   1.0000   0.9773
## Pos Pred Value         1.0000   1.0000   0.9091
## Neg Pred Value         1.0000   0.9706   1.0000
## Prevalence             0.4259   0.3889   0.1852
## Detection Rate         0.4259   0.3704   0.1852
## Detection Prevalence   0.4259   0.3704   0.2037
## Balanced Accuracy      1.0000   0.9762   0.9886

7. KNN & Logistic

Heat Data 를 KNN 과 Logistic 를 사용해보자

범주형 변수는 factor 형으로 변환 시켜, df_1 테이블 생성

##      age      sex       cp trestbps     chol      fbs  restecg  thalach 
##        0        0        0        0        0        0        0        0 
##    exang  oldpeak    slope       ca     thal   target 
##        0        0        0        0        0        0
##       age        sex     cp         trestbps          chol       fbs    
##  Min.   :29.00   0: 96   0:143   Min.   : 94.0   Min.   :126.0   0:258  
##  1st Qu.:47.50   1:207   1: 50   1st Qu.:120.0   1st Qu.:211.0   1: 45  
##  Median :55.00           2: 87   Median :130.0   Median :240.0          
##  Mean   :54.37           3: 23   Mean   :131.6   Mean   :246.3          
##  3rd Qu.:61.00                   3rd Qu.:140.0   3rd Qu.:274.5          
##  Max.   :77.00                   Max.   :200.0   Max.   :564.0          
##     restecg          thalach      exang      oldpeak         slope      
##  Min.   :0.0000   Min.   : 71.0   0:204   Min.   :0.00   Min.   :0.000  
##  1st Qu.:0.0000   1st Qu.:133.5   1: 99   1st Qu.:0.00   1st Qu.:1.000  
##  Median :1.0000   Median :153.0           Median :0.80   Median :1.000  
##  Mean   :0.5281   Mean   :149.6           Mean   :1.04   Mean   :1.399  
##  3rd Qu.:1.0000   3rd Qu.:166.0           3rd Qu.:1.60   3rd Qu.:2.000  
##  Max.   :2.0000   Max.   :202.0           Max.   :6.20   Max.   :2.000  
##        ca         thal    target 
##  Min.   :0.0000   0:  2   0:138  
##  1st Qu.:0.0000   1: 18   1:165  
##  Median :0.0000   2:166          
##  Mean   :0.7294   3:117          
##  3rd Qu.:1.0000                  
##  Max.   :4.0000

thal 변수에 0 이 2개 있는데 확인해보기

##   age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca thal
## 1  53   0  2      128  216   0       0     115     0       0     2  0    0
## 2  52   1  0      128  204   1       1     156     1       1     1  0    0
##   target
## 1      1
## 2      0

thal의 0 은 결측 인것으로 판단됨 thal 이 0 이 아닌것만 추출

###KNN Modelling

KNN 모델에 사용되는 변수들은 숫자형이기 때문에

  1. as.numeric 변환
  2. scaling 적용

KNN 활용의 특징

  1. Heart 데이터는 1,2 로 구분되어 있는 2가지 범주 예측 기존 train 함수를 사용해서는 2 class 분할이 되지 않는다 그래서 KNN 함수를 사용해서 모델링 완료
  1. 데이터 분할

  2. 분할된 데이터를 숫자형으로 변환

  3. 1-13 목표 변수를 제외한 모든 변수를 train/test 마다 scale 를 직접 진행 scale 함수를 사용

  4. Target 변수를 labeling 따로 지정(train, test 각각)

  5. Optimal K 지정 가능 round(sqrt(nrow ))

  6. knn 함수 예측 시작 (train/ test/ cl/ k) 지정

  7. Confusion Matrix 정확도 확인

    KNN 함수를 따로 적용하면 따로 따로 해야할 일이 많음 Caret 이 train 함수를 적용할 때에는 3가지 범주는 자동적으로 적용 아니면 이렇게 전부 손수 코드 작성해야한다.

## [1] 14
## [1] 2 2 2 2 2 2
## Levels: 1 2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2
##          1 35  5
##          2 10 41
##                                           
##                Accuracy : 0.8352          
##                  95% CI : (0.7427, 0.9047)
##     No Information Rate : 0.5055          
##     P-Value [Acc > NIR] : 5.352e-11       
##                                           
##                   Kappa : 0.6699          
##                                           
##  Mcnemar's Test P-Value : 0.3017          
##                                           
##             Sensitivity : 0.8913          
##             Specificity : 0.7778          
##          Pos Pred Value : 0.8039          
##          Neg Pred Value : 0.8750          
##              Prevalence : 0.5055          
##          Detection Rate : 0.4505          
##    Detection Prevalence : 0.5604          
##       Balanced Accuracy : 0.8345          
##                                           
##        'Positive' Class : 2               
## 

의견) KNN 은 2 개의 범주형 모델에는 적합한 모델로 보여지지 않는다.

LS0tDQp0aXRsZTogIlN1cGVydmlzZWQgTWFjaGluZSBMZWFybmluZyINCmF1dGhvcjogIkRPRVVOIg0KZGF0ZTogIjAyLzAyLzIwMjEiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAjIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgICMgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogImZsYXRseSINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRSwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEwKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiLCBkZXBlbmRlbmNpZXM9VFJVRSkgDQojaW5zdGFsbC5wYWNrYWdlcygicnNhbXBsZSIpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJzYW1wbGUpDQpsaWJyYXJ5KGNsYXNzKQ0KYGBgDQoNCiMgMS4gS05OIC0gV0lORSANCg0KIENhcmV0IHBhY2thZ2UgDQogDQogdHJhaW5Db250cm9sKCkgLSDtm4jroKjqs7zsoJUg7KSRIHBhcmFtZXRlciDshKTsoJUgDQogICBFeCkgdHJhaW5Db250cm9sICggbWV0aG9kID0gInJlcGVhdGVkY3YiDQogICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAsICA8LSDtm4jroKjrjbDsnbTthLAgZm9sZCDqsK/siJggDQogICAgICAgICAgICAgICAgICAgICAgcmVwZWF0IC0gNSkgICAgPC0gY3Yg67CY67O1IO2an+yImCANCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICANCiBleHBhbmQuZ3JpZCgpIC0gZmFjdG9yIOyhsO2VqeydmCDrjbDsnbTthLAg7ZSE66CI7J6EIOyDneyEsSANCiAgIEV4KSBleHBhbmQuZ3JpZCAoaz0xOjEwKQ0KICAgDQogICANCiB0cmFpbigpDQogIEV4KSB0cmFpbiAoIENsYXNzIH4uICwgDQogICAgICAgICAgICAgIGRhdGEgPSANCiAgICAgICAgICAgICAgbWV0aG9kPQ0KICAgICAgICAgICAgICB0ckNvbnRyYW9sIA0KICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwic2NhbGUiKSwgPC0g7ZGc7KSA7ZmULCANCiAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChrPTE6MTApLCAgPC0g7YyM652866+47YSw6rCSIOuqqeuhnQ0KICAgICAgICAgICAgICBtZXRyaWM9IkFjY3VhcmN5KSAgICA8LSDrqqjtmJXrsKnsi50gDQogICAgICAgICAgICAgIA0KICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIA0KIEFjY3VyYXJjeSDsoJXtmZXrj4QgPSBUUCtUTi8gVG90YWwgIA0KICANCiBLYXBwYSDthrXqs4Trn4kgID0gcDAtcGUvMS1wZSANCiAgICAgICAgICAgICAgICAgIHAwOiDqtIDsuKEg7KCV7ZmV64+EIC8gcGU6IOq4sOuMgCDsoJXtmZXrj4QgDQoNCmBgYHtyfQ0KDQpzZXR3ZCgnQzovVXNlcnMvQWRtaW5pc3RyYXRvci9EZXNrdG9wL1IgQW5hbHlzaXMvRmFzdCBDYW1wdXMvUGFydCA2L0NoMDIuIGstTmVhcmVzdCBOZWlnaGJvci9EYXRhJykNCg0KDQpyZWFkLmNzdigid2luZS5jc3YiLGhlYWRlciA9IFRSVUUpIC0+IHJhd2RhdGENCg0KcmF3ZGF0YSRDbGFzcyA8LSBhcy5mYWN0b3IocmF3ZGF0YSRDbGFzcykgI2ZhY3RvciDrs4DtmZggDQpzdHIocmF3ZGF0YSkNCg0KZGF0YXRhYmxlKHJhd2RhdGEpDQoNCg0KYGBgDQoNCioqKlRyYWluLCBUZXN0IOu2hO2VoCoqKg0KDQpgYGB7cn0NCg0KYW5hbGRhdGEgPC1yYXdkYXRhDQoNCg0Kc2V0LnNlZWQoMjAyMCkgI1NlZWQg7ISk7KCVIA0KZGF0YXRvdGFsIDwtIHNvcnQoc2FtcGxlKG5yb3coYW5hbGRhdGEpLCBucm93KGFuYWxkYXRhKSowLjcpKSAjc2FtcGxlIO2RnOuzuCDrvZHquLAgMTAw7KSR7JeQIDcw6rCcIOu9keq4sCAgDQoNCiNzYW1wbGUoYSxiKSA6IDHrtoDthLAgYeq5jOyngCDsiKvsnpAg7KSR7JeQIGLqsJwg7LaU7LacIA0KDQp0cmFpbiA8LXJhd2RhdGFbZGF0YXRvdGFsLF0NCnRlc3QgPC1yYXdkYXRhWy1kYXRhdG90YWwsIF0NCg0KDQp0cmFpbl94IDwtdHJhaW5bLDE6MTNdDQp0cmFpbl95IDwtdHJhaW5bLDE0XQ0KDQp0ZXN0X3ggPC10ZXN0WywxOjEzXQ0KdGVzdF95IDwtdGVzdFssMTRdDQpgYGANCg0KKioq66qo7ZiVKioqIA0KYGBge3J9DQoNCmN0cmwgPC10cmFpbkNvbnRyb2wobWV0aG9kID0icmVwZWF0ZWRjdiIsIA0KICAgICAgICAgICAgICAgICAgICBudW1iZXIgPTEwLCANCiAgICAgICAgICAgICAgICAgICAgcmVwZWF0cz01KQ0KDQoNCmN1c3RvbUdyaWQgPC1leHBhbmQuZ3JpZChrPTE6MTApDQoNCnRyYWluKENsYXNzfi4sIA0KICAgICAgZGF0YT10cmFpbiwNCiAgICAgIG1ldGhvZCA9ICJrbm4iLCANCiAgICAgIHRyQ29udHJvbCA9IGN0cmwsIA0KICAgICAgcHJlUHJvY2Vzcz0gYygiY2VudGVyIiwic2NhbGUiKSwgDQogICAgICB0dW5lR3JpZCA9IGN1c3RvbUdyaWQsIA0KICAgICAgbWV0cmljPSJBY2N1cmFjeSIpIC0+IGtubkZpdA0KDQpgYGANCg0KDQpgYGB7cn0NCmtubkZpdA0KYGBgDQoNCg0KYGBge3J9DQpwbG90KGtubkZpdCkNCmBgYA0KDQoqKipQcmVkaWN0aW9uKioqDQoNCmBgYHtyfQ0KDQpwcmVkX3Rlc3QgPC1wcmVkaWN0KGtubkZpdCwgbmV3ZGF0YSA9IHRlc3QpDQoNCmNvbmZ1c2lvbk1hdHJpeChwcmVkX3Rlc3QsIHRlc3QkQ2xhc3MpDQpgYGANCg0KKioqSW1wb3J0YW5jZSBGZWF0dXJlKioqDQoNCmBgYHtyfQ0KDQp2YXJJbXAoa25uRml0LCBzY2FsZSA9IEZBTFNFKSAtPmltcG9ydGFuY2Vfa25uDQpwbG90KGltcG9ydGFuY2Vfa25uKQ0KYGBgDQoNCg0KIyAyLiBMb2dpc3RpYyAtIEhlYXJ0QXR0YWNrIA0KDQpgYGB7cn0NCnNldHdkKCdDOi9Vc2Vycy9BZG1pbmlzdHJhdG9yL0Rlc2t0b3AvUiBBbmFseXNpcy9GYXN0IENhbXB1cy9QYXJ0IDYvQ2gwMy4gTG9naXN0aWMgUmVncmVzc2lvbi9EYXRhJykNCnJlYWQuY3N2KCJoZWFydC5jc3YiLCBoZWFkZXIgPSBUUlVFKSAtPiBoZWFydA0KDQpzdHIoaGVhcnQpDQoNCiMw6rO8IDHroZwg64KY7YOA64KcIOuzgOyImOuTpOydmCDsoITsspjrpqzqsIAg7ZWE7JqU7ZWoIA0KDQpoZWFydCR0YXJnZXQgPC1hcy5mYWN0b3IoaGVhcnQkdGFyZ2V0KQ0KdW5pcXVlKGhlYXJ0JHRhcmdldCkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIOyXsOyGje2YlSDrs4DsiJjripQg7ZGc7KSA7ZmUIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmhlYXJ0JGFnZSAgIDwtICAgc2NhbGUoaGVhcnQkYWdlKQ0KaGVhcnQkdHJlc3RicHMgIDwtICBzY2FsZShoZWFydCR0cmVzdGJwcykNCmhlYXJ0JGNob2w8LSAgc2NhbGUoaGVhcnQkY2hvbCkNCmhlYXJ0JHRoYWxhY2ggPC0gc2NhbGUoaGVhcnQkdGhhbGFjaCkNCmhlYXJ0JG9sZHBlYWs8LSAgc2NhbGUoaGVhcnQkb2xkcGVhaykNCmhlYXJ0JHNsb3BlIDwtIHNjYWxlKGhlYXJ0JHNsb3BlKQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMg67KU7KO8IOuzgOyImOuKlCBhcy5mYWN0b3IgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KbmV3ZGF0YTwtaGVhcnQNCmZhY3RvclZhciA8LSBjKCJzZXgiLCAiY3AiLCAicmVzdGVjZyIsICJleGFuZyIsImNhIiwidGhhbCIpDQpuZXdkYXRhWyxmYWN0b3JWYXJdID0gbGFwcGx5KG5ld2RhdGFbLGZhY3RvclZhcl0sIGZhY3RvcikNCg0KDQoNCg0KYGBgDQoNClRyYW4sIFRlc3Qg64KY64iE6riwIA0KDQpgYGB7cn0NCg0Kc2V0LnNlZWQoMjAyMCkNCg0Kc2FtcGxlIDwtIHNvcnQoc2FtcGxlKG5yb3cobmV3ZGF0YSksbnJvdyhuZXdkYXRhKSowLjcpKQ0KDQp0cmFpbiA8LSBuZXdkYXRhW3NhbXBsZSxdDQp0ZXN0IDwtIG5ld2RhdGFbLXNhbXBsZSxdDQoNCnRyYWluX3ggPC10cmFpblssMToxMl0NCnRyYWluX3kgPC0gdHJhaW5bLDEzXQ0KDQp0ZXN0X3ggPC0gdGVzdFssMToxMl0NCnRlc3RfeSA8LSB0ZXN0WywxM10NCg0KYGBgDQoNCg0KKioqTW9kZWxsaW5nKioqDQoNCmBgYHtyfQ0KDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAicmVwZWF0ZWRjdiIsIHJlcGVhdHMgPSA1KQ0KDQp0cmFpbih0YXJnZXR+LiwgDQogICAgICBkYXRhPXRyYWluLCANCiAgICAgIG1ldGhvZCA9ICJMb2dpdEJvb3N0IiwgDQogICAgICB0ckNvbnRybCA9IGN0cmwsIA0KICAgICAgbWV0cmljID0gIkFjY3VyYWN5IikgLT4gbG9naXRGaXQNCg0KDQpsb2dpdEZpdA0KYGBgDQoNCg0KYGBge3J9DQoNCnBsb3QobG9naXRGaXQpDQpgYGANCg0KDQpgYGB7cn0NCg0KcHJlZGljdChsb2dpdEZpdCwgbmV3ZGF0YSA9IHRlc3QpIC0+cHJlZF90ZXN0DQoNCmNvbmZ1c2lvbk1hdHJpeChwcmVkX3Rlc3QsIHRlc3QkdGFyZ2V0KQ0KYGBgDQoNCioqKkltcG9ydGFuY2UgVmFyaWFibGVzKioqDQpgYGB7cn0NCg0KdmFySW1wKGxvZ2l0Rml0LCBzY2FsZSA9IEZBTFNFKSAtPiBpbXBvcnRhbmNlX2xvZ2l0DQoNCnBsb3QoaW1wb3J0YW5jZV9sb2dpdCkNCmBgYA0KDQoNCiMgMy4gTmFpdmVfYmF5ZXMgLSBXSU5FIA0KDQpgYGB7cn0NCg0Kc2V0d2QoJ0M6L1VzZXJzL0FkbWluaXN0cmF0b3IvRGVza3RvcC9SIEFuYWx5c2lzL0Zhc3QgQ2FtcHVzL1BhcnQgNi9DaDAyLiBrLU5lYXJlc3QgTmVpZ2hib3IvRGF0YScpDQoNCg0KcmVhZC5jc3YoIndpbmUuY3N2IiwgaGVhZGVyID0gVCkgLT4gd2luZQ0KDQoNCndpbmUkQ2xhc3MgPC0gYXMuZmFjdG9yKHdpbmUkQ2xhc3MpDQpzdHIod2luZSkNCmBgYA0KDQoNCioqKlRSQUlOL1RFU1Qg64KY64iE6riwKioqDQoNCmBgYHtyfQ0KDQpzYW1kYXRhIDwtd2luZQ0KDQpzZXQuc2VlZCgyMDIwKQ0KDQpzb3J0KHNhbXBsZShucm93KHNhbWRhdGEpLCBucm93KHNhbWRhdGEpKi43KSktPiBzYW1wbGVzDQoNCnRyYWluIDwtd2luZVtzYW1wbGVzLF0NCnRlc3QgPC13aW5lWy1zYW1wbGVzLF0NCg0KdHJhaW5feCA8LXRyYWluWywxOjEzXQ0KdHJhaW5feSA8LXRyYWluWywgMTRdDQoNCnRlc3RfeCA8LXRlc3RbLCAxOjEzXQ0KdGVzdF95IDwtdGVzdFssMTRdDQoNCg0KYGBgDQoNCg0KKioqTW9kZWxsaW5nKioqDQoNCmBgYHtyfQ0KDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2Q9InJlcGVhdGVkY3YiLHJlcGVhdHMgPSA1KQ0KbmJGaXQgPC0gdHJhaW4oQ2xhc3MgfiAuLA0KICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJuYWl2ZV9iYXllcyIsDQogICAgICAgICAgICAgICAgdHJDb250cm9sID0gY3RybCwNCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsInNjYWxlIiksDQogICAgICAgICAgICAgICAgbWV0cmljPSJBY2N1cmFjeSIpDQpuYkZpdA0KYGBgDQoNCg0KYGBge3J9DQoNCnBsb3QobmJGaXQpDQpgYGANCg0K7Luk64SQ7J2AIOyCrOyaqSB4LCDsu6TrhJAgbyDsl5Ag65Sw66W4IOygle2ZleuPhOulvCDrs7Tsl6zspIwNCg0KDQpgYGB7cn0NCnByZWRpY3QobmJGaXQsIG5ld2RhdGEgPSB0ZXN0KSAtPiBwcmVkX3Rlc3QNCg0KY29uZnVzaW9uTWF0cml4KHByZWRfdGVzdCwgdGVzdCRDbGFzcykNCmBgYA0KDQoNCg0KYGBge3J9DQoNCmltcF92YXIgPC0gdmFySW1wKG5iRml0LCBzY2FsZT1GKQ0KcGxvdChpbXBfdmFyKQ0KYGBgDQoNClJPQyDsu6TruIwg6riw7KSA7Jy866GcIOuzgOyImCDspJHsmpTrj4Trpbwg7ISg67OE7ZW07IScIOuztOyXrOykgOuLpC4gDQpST0Mg7Luk67iMIOuptOyggeydtCDrhJPsnYQg7IiY66GdIOykkeyalOuPhOqwgCDsg4HsirntlZzri6QuIA0KDQoNCiMgNC4gRGVzY2lzaW9uIFRyZWUNCg0KKioq6riw67O4IO2KuOumrCDshKTsoJUqKioNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInRyZWUiKQ0KbGlicmFyeSh0cmVlKQ0Kc2V0d2QoJ0M6L1VzZXJzL0FkbWluaXN0cmF0b3IvRGVza3RvcC9SIEFuYWx5c2lzL0Zhc3QgQ2FtcHVzL1BhcnQgNi9DaDAyLiBrLU5lYXJlc3QgTmVpZ2hib3IvRGF0YScpDQoNCg0KcmVhZC5jc3YoIndpbmUuY3N2IixoZWFkZXIgPSBUUlVFKSAtPiByYXdkYXRhDQoNCnJhd2RhdGEkQ2xhc3MgPC0gYXMuZmFjdG9yKHJhd2RhdGEkQ2xhc3MpDQoNCmRlZGF0YSA8LSByYXdkYXRhDQoNCnNldC5zZWVkKDIwMjApDQoNCnNhbXBsZV9kYXRhIDwtIHNvcnQoc2FtcGxlKG5yb3coZGVkYXRhKSwgbnJvdyhkZWRhdGEpKi43KSkNCg0KDQp0cmFpbiA8LWRlZGF0YVtzYW1wbGVfZGF0YSxdDQp0ZXN0IDwtZGVkYXRhWy1zYW1wbGVfZGF0YSwgXQ0KDQoNCnRyYWluX3ggPC10cmFpblssMToxM10NCnRyYWluX3kgPC10cmFpblssMTRdDQoNCnRlc3RfeCA8LXRlc3RbLDE6MTNdDQp0ZXN0X3kgPC10ZXN0WywxNF0NCg0KdHJlZShDbGFzc34uLCBkYXRhPXRyYWluKSAtPnRyZWVSYXcNCg0KcGxvdCh0cmVlUmF3KQ0KdGV4dCh0cmVlUmF3KQ0KYGBgDQoNCioqKkNyb3NzLVZhbGlkYXRpb24qKioNCmBgYHtyfQ0KY3ZfdHJlZTwtY3YudHJlZSh0cmVlUmF3LCBGVU49cHJ1bmUubWlzY2xhc3MpICPsmKTrtoTrpZgg6riw7KSAIA0KDQpwbG90KGN2X3RyZWUpDQpgYGANCg0KDQoqKipQcnVuaW5nIOqwgOyngOy5mOq4sCoqKg0KDQpgYGB7cn0NCg0KcHJ1bmUubWlzY2xhc3ModHJlZVJhdywgYmVzdD00KSAtPiBwcnVuZV90cmVlDQpwbG90KHBydW5lX3RyZWUpDQp0ZXh0KHBydW5lX3RyZWUsIHByZXR0eT0wKSAjcHJldHR5PTAg67aE7ZWgIGZlYXR1cmUg7J2YIOydtOumhOydhCDrsJTqvrjsp4Ag7JWK64qU64ukLiANCmBgYA0KDQoqKipQcmVkaWNpdG9uKioqDQpgYGB7cn0NCg0KcHJlZGljdChwcnVuZV90cmVlLCB0ZXN0LCB0eXBlPSdjbGFzcycpIC0+IHByZWRfdGVzdA0KY29uZnVzaW9uTWF0cml4KHByZWRfdGVzdCwgdGVzdCRDbGFzcykNCg0KYGBgDQoNCiMgNS4gUmFuZG9tIEZvcmVzdCANCg0KYGBge3J9DQoNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgcmVwZWF0cyA9IDUpDQoNCnRyYWluKENsYXNzfi4sIA0KICAgICAgZGF0YT10cmFpbiwgDQogICAgICBtZXRob2QgPSJyZiIsIA0KICAgICAgdHJDb250cm9sPWN0cmwsIA0KICAgICAgcHJlUHJvY2Vzcz0gYygiY2VudGVyIiwgInNjYWxlIiksIA0KICAgICAgbWV0cmljPSAiQWNjdXJhY3kiKSAtPiByZkZpdA0KDQoNCnJmRml0DQpgYGANCg0KDQpgYGB7cn0NCnZhckltcChyZkZpdCkgLT4gaW1wDQpwbG90KGltcCkNCmBgYA0KDQojIDYuIFNWTSBMaW5lYXIgDQoNCmBgYHtyfQ0Kc2V0d2QoJ0M6L1VzZXJzL0FkbWluaXN0cmF0b3IvRGVza3RvcC9SIEFuYWx5c2lzL0Zhc3QgQ2FtcHVzL1BhcnQgNi9DaDAyLiBrLU5lYXJlc3QgTmVpZ2hib3IvRGF0YScpDQoNCnJlYWQuY3N2KCJ3aW5lLmNzdiIsaGVhZGVyID0gVFJVRSkgLT4gd2luZQ0KDQp3aW5lJENsYXNzIDwtYXMuZmFjdG9yKHdpbmUkQ2xhc3MpDQoNCnN0cih3aW5lKQ0KDQp3aW5lIC0+IHN2bV93aW5lDQoNCg0Kc2V0LnNlZWQoMjAyMSkNCg0Kd2luZV9zYW0gPC0gc29ydChzYW1wbGUobnJvdyhzdm1fd2luZSksIG5yb3coc3ZtX3dpbmUpKi43KSkNCg0KDQp0cmFpbiA8LSBzdm1fd2luZVt3aW5lX3NhbSxdDQp0ZXN0IDwtIHN2bV93aW5lWy13aW5lX3NhbSxdDQoNCg0KDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSJyZXBlYXRlZGN2IiwgcmVwZWF0cz01KQ0KDQp0cmFpbihDbGFzc34uLCANCiAgICAgIGRhdGE9dHJhaW4sIA0KICAgICAgbWV0aG9kID0gInN2bUxpbmVhciIsICNzdm1Qb2x5IOu5hOyEoO2YlSBzdm0gDQogICAgICB0ckNvbnRyb2w9Y3RybCwgDQogICAgICBwcmVQcm9jZXNzPSBjKCJjZW50ZXIiLCJzY2FsZSIpLCANCiAgICAgIG1ldHJpYyA9IkFjY3VyYWN5IikgLT5zdm1fRml0DQoNCg0KDQpzdm1fRml0DQpgYGANCg0KDQpgYGB7cn0NCg0KcHJlZGljdChzdm1fRml0LCBuZXdkYXRhID0gdGVzdCkgLT4gcHJlZF90ZXN0DQpjb25mdXNpb25NYXRyaXgocHJlZF90ZXN0LCB0ZXN0JENsYXNzKQ0KYGBgDQoNCg0KYGBge3J9DQoNCnZhckltcChzdm1fRml0LCBzY2FsZSA9IEZBTFNFKS0+IGltcA0KcGxvdChpbXApDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQojIDcuIEtOTiAmIExvZ2lzdGljIA0KDQpIZWF0IERhdGEg66W8IEtOTiDqs7wgTG9naXN0aWMg66W8IOyCrOyaqe2VtOuztOyekCANCg0K67KU7KO87ZiVIOuzgOyImOuKlCBmYWN0b3Ig7ZiV7Jy866GcIOuzgO2ZmCDsi5zsvJwsIGRmXzEg7YWM7J2067iUIOyDneyEsSANCg0KYGBge3J9DQoNCnNldHdkKCdDOi9Vc2Vycy9BZG1pbmlzdHJhdG9yL0Rlc2t0b3AvUiBBbmFseXNpcy9GYXN0IENhbXB1cy9QYXJ0IDYvQ2gwMy4gTG9naXN0aWMgUmVncmVzc2lvbi9EYXRhJykNCnJlYWQuY3N2KCJoZWFydC5jc3YiLCBoZWFkZXIgPSBUUlVFKSAtPiBoZWFydA0KDQpoZWFydCAlPiUgDQogIG11dGF0ZShzZXg9IGFzLmZhY3RvcihzZXgpLCANCiAgICAgICAgIGNwID0gYXMuZmFjdG9yKGNwKSwgDQogICAgICAgICBmYnMgPSBhcy5mYWN0b3IoZmJzKSwgDQogICAgICAgICBleGFuZz0gYXMuZmFjdG9yKGV4YW5nKSwgDQogICAgICAgICB0aGFsID0gYXMuZmFjdG9yKHRoYWwpLCANCiAgICAgICAgIHRhcmdldD0gYXMuZmFjdG9yKHRhcmdldCkpIC0+IGRmXzENCiAgDQoNCiMg6rKw7Lih7LmYIO2ZleyduO2VtOuztOq4sCANCmNvbFN1bXMoaXMubmEoZGZfMSkpDQpgYGANCg0KDQpgYGB7cn0NCg0Kc3VtbWFyeShkZl8xKQ0KYGBgDQoNCnRoYWwg67OA7IiY7JeQIDAg7J20IDLqsJwg7J6I64qU642wIO2ZleyduO2VtOuztOq4sCANCg0KDQpgYGB7cn0NCg0KZGZfMSAlPiUgIA0KICBmaWx0ZXIodGhhbCA9PTApDQpgYGANCg0KDQp0aGFs7J2YIDAg7J2AIOqysOy4oSDsnbjqsoPsnLzroZwg7YyQ64uo65CoIHRoYWwg7J20IDAg7J20IOyVhOuLjOqyg+unjCDstpTstpwNCg0KYGBge3J9DQoNCmRmXzEgJT4lIA0KICBmaWx0ZXIodGhhbCAhPTApIC0+IGRmXzENCmBgYA0KDQoNCiMjI0tOTiBNb2RlbGxpbmcgDQoNCktOTiDrqqjrjbjsl5Ag7IKs7Jqp65CY64qUIOuzgOyImOuTpOydgCDsiKvsnpDtmJXsnbTquLAg65WM66y47JeQDQoNCjEpIGFzLm51bWVyaWMg67OA7ZmYIA0KMikgc2NhbGluZyDsoIHsmqkgDQoNCiANCiANCiANCktOTiDtmZzsmqnsnZgg7Yq57KeVIA0KDQoxLiBIZWFydCDrjbDsnbTthLDripQgMSwyIOuhnCDqtazrtoTrkJjslrQg7J6I64qUIDLqsIDsp4Ag67KU7KO8IOyYiOy4oSANCiAgIOq4sOyhtCB0cmFpbiDtlajsiJjrpbwg7IKs7Jqp7ZW07ISc64qUIDIgY2xhc3Mg67aE7ZWg7J20IOuQmOyngCDslYrripTri6QNCiAgIOq3uOuemOyEnCBLTk4g7ZWo7IiY66W8IOyCrOyaqe2VtOyEnCDrqqjrjbjrp4Eg7JmE66OMIA0KICAgDQoxKSDrjbDsnbTthLAg67aE7ZWgIA0KMikg67aE7ZWg65CcIOuNsOydtO2EsOulvCDsiKvsnpDtmJXsnLzroZwg67OA7ZmYIA0KMykgMS0xMyDrqqntkZwg67OA7IiY66W8IOygnOyZuO2VnCDrqqjrk6Ag67OA7IiY66W8IHRyYWluL3Rlc3Qg66eI64ukIHNjYWxlIOulvCDsp4HsoJEg7KeE7ZaJIHNjYWxlIO2VqOyImOulvCDsgqzsmqkgDQo0KSBUYXJnZXQg67OA7IiY66W8IGxhYmVsaW5nIOuUsOuhnCDsp4DsoJUodHJhaW4sIHRlc3Qg6rCB6rCBKQ0KNSkgT3B0aW1hbCBLIOyngOyglSDqsIDriqUgcm91bmQoc3FydChucm93ICkpDQo2KSBrbm4g7ZWo7IiYIOyYiOy4oSDsi5zsnpEgKHRyYWluLyB0ZXN0LyBjbC8gaykg7KeA7KCVIA0KNykgQ29uZnVzaW9uIE1hdHJpeCDsoJXtmZXrj4Qg7ZmV7J24IA0KICAgICAgICANCiAgICAgICAgDQogICAgICAgIA0KICAgIEtOTiDtlajsiJjrpbwg65Sw66GcIOyggeyaqe2VmOuptCDrlLDroZwg65Sw66GcIO2VtOyVvO2VoCDsnbzsnbQg66eO7J2MIA0KICAgIENhcmV0IOydtCB0cmFpbiDtlajsiJjrpbwg7KCB7Jqp7ZWgIOuVjOyXkOuKlCAz6rCA7KeAIOuylOyjvOuKlCDsnpDrj5nsoIHsnLzroZwg7KCB7JqpIA0KICAgIOyVhOuLiOuptCDsnbTroIfqsowg7KCE67aAIOyGkOyImCDsvZTrk5wg7J6R7ISx7ZW07JW87ZWc64ukLiANCg0KYGBge3J9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIOu2hO2VoCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQpmbGFnIDwtIHNvcnQoc2FtcGxlKG5yb3coZGZfMSksIG5yb3coZGZfMSkqMC43KSkNCg0KdHJhaW5faCA8LSBkZl8xW2ZsYWcsXQ0KdGVzdF9oIDwtIGRmXzFbLWZsYWcsXQ0KDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIOu2hO2VoOuQnCAg6rCB6rCB7J2YIHRyYWluLCB0ZXN0IOyXkCBhcy5udW1lcmljIOyggeyaqSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQp0cmFpbl9oZWFydDwtIHRyYWluX2ggJT4lIA0KICBtdXRhdGUoc2V4ID0gYXMubnVtZXJpYyhzZXgpLA0KICAgICAgICAgY3AgPSBhcy5udW1lcmljKGNwKSwNCiAgICAgICAgIGZicyA9IGFzLm51bWVyaWMoZmJzKSwNCiAgICAgICAgIGV4YW5nID0gYXMubnVtZXJpYyhleGFuZyksDQogICAgICAgICB0aGFsID0gYXMubnVtZXJpYyh0aGFsKSwNCiAgICAgICAgIHRhcmdldCA9IGFzLm51bWVyaWModGFyZ2V0KSkNCg0KDQp0ZXN0X2hlYXJ0PC0gdGVzdF9oICU+JSANCiAgbXV0YXRlKHNleCA9IGFzLm51bWVyaWMoc2V4KSwNCiAgICAgICAgIGNwID0gYXMubnVtZXJpYyhjcCksDQogICAgICAgICBmYnMgPSBhcy5udW1lcmljKGZicyksDQogICAgICAgICBleGFuZyA9IGFzLm51bWVyaWMoZXhhbmcpLA0KICAgICAgICAgdGhhbCA9IGFzLm51bWVyaWModGhhbCksDQogICAgICAgICB0YXJnZXQgPSBhcy5udW1lcmljKHRhcmdldCkpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIFRhcmdldCDrs4DsiJjrpbwg7KCc7Jm47ZWcIOuCmOuouOyngOyXkCBzY2FsZSDsoIHsmqkgIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCnRyYWluIDwtIHNjYWxlKHg9dHJhaW5faGVhcnRbLCAtMTRdKQ0KdGVzdCA8LSBzY2FsZSh4PXRlc3RfaGVhcnRbLC0xNF0sIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBhdHRyKHRyYWluLCAic2NhbGVkOmNlbnRlciIpLCANCiAgICAgICAgICAgICAgc2NhbGUgPSBhdHRyKHRyYWluLCAic2NhbGVkOnNjYWxlIikpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgTGFiZWwg7ISk7KCVIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQp0cl9sYWJlbCA8LSB0cmFpbl9oZWFydFssMTRdDQp0ZV9sYWJlbCA8LSB0ZXN0X2hlYXJ0WywxNF0NCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBPcHRpbWFsIEsgPSAxNSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQpyb3VuZChzcXJ0KG5yb3codHJhaW5faGVhcnQpKSwwKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIEtOTiDtlajsiJgg7IKs7JqpIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmtubih0cmFpbj10cmFpbiwNCiAgICB0ZXN0PXRlc3QsIA0KICAgIGNsID0gdHJfbGFiZWwsDQogICAgaz0xNSkgLT5LTk5fRklUDQoNCmhlYWQoS05OX0ZJVCkNCg0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQp0ZV9sYWJlbCA8LWFzLmZhY3Rvcih0ZV9sYWJlbCkNCg0KDQpjb25mdXNpb25NYXRyaXgoZGF0YT1LTk5fRklULCANCiAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSB0ZV9sYWJlbCwgDQogICAgICAgICAgICAgICAgcG9zaXRpdmU9ICIyIikNCg0KYGBgDQoNCg0K7J2Y6rKsKSANCktOTiDsnYAgMiDqsJzsnZgg67KU7KO87ZiVIOuqqOuNuOyXkOuKlCDsoIHtlantlZwg66qo642466GcIOuztOyXrOyngOyngCDslYrripTri6QuIA0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg==