목차


정형 데이터 마이닝

정형, 비정형 데이터의 정의

데이터마이닝

앞에서 언급 하였듯 데이터마이닝은 거대한 양의 데이터 속에 쉽게 드러나지 않는 통계적 규칙이나 패턴과 같은 유용한 정보를 찾아내는 과정이라 말할 수 있다. 데이터 내에 정보를 찾는 것이 주 목적

데이터마이닝의 종류

  • 분류 : 새롭게 나타나는 현상을 기존 정의된 집합에 배정하는 것을 의미
  • 추정 : 수입, 수준, 신용잔고처럼 연속된 변수의 값을 추정하는 것을 의미
  • 예측 : 미래의 값을 추정하거나 분류하는 것을 의미
  • 군집 : 이질적인 모집단을 동질성을 지닌 그룹별로 세분화 하는 것을 의미
  • 연관 : 같이 팔리는 물건들 사이의 연관성을 파악하는 분석

iris data 설명

iris 자료는 R에서 기본으로 제공하는 붓꽃에 대한 자료

변수

  • Sepal.Length : 꽃받침의 길이정보
  • Sepal.Width : 꽃받침의 너비정보
  • Petal.Length : 꽃잎의 길이정보
  • Petal.Width : 꽃잎의 너비정보
  • Species : 꽃의 종류(setosa, versicolor, virginica)

setosa와 versicolor를 분류하는 모형을 만들려고 한다. 이에 따라 필요한 데이터를 추출


data(iris)
data=iris
summary(data)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  
boxplot(iris[,-5])

분류 모형

기존 자료에 대해 학습한 모형을 바탕으로 새롭게 나타나는 자료에 대하여 정의된 집합에 배정하는 것 모형을 의미한다.

데이터 정재 - 모형 생성 - 모형평가 및 모형비교 순으로 진행

로지스틱 회귀모형(logistic model)

로지스틱 회귀는 분석 대상들이 여러 집단으로 나누어진 경우, 독립 변수의 선형 결합을 이용하여 개별 관측치가 어느 집단에 속하는지 확률을 계산하는 분류 기법이다. 로지스틱이란 odds에 log를 취한 값으로 로지스틱 회귀모형은 아래와 같은 수식으로 나타난다.

\(\ln(\frac{P}{1-P})=a+bX\)

\(odds =\frac{어떤일이\ 일어날\ 확률}{어떤\ 일이\ 일어나지\ 않을\ 확률}\)

로지스틱회귀 모형을 사용하여 setosa와 versicolor를 분류

#데이터 추출
data<-subset(data,Species=="setosa"|Species=="versicolor")
data$Species<-factor(data$Species)
#모형생성
Species_glm<-glm(Species~.,data=data,family=binomial)
# Species_glm<-glm(Species~.,data=data,family=binomial)
# Species_glm<-glm(Species~.-Sepal.Length,data=data,family=binomial)
# Species_glm<-glm(Species~.+I(Sepal.Length^2),data=data,family=binomial)
#모형 요약
summary(Species_glm)

Call:
glm(formula = Species ~ ., family = binomial, data = data)

Deviance Residuals: 
       Min          1Q      Median          3Q         Max  
-1.681e-05  -2.110e-08   0.000e+00   2.110e-08   2.006e-05  

Coefficients:
               Estimate Std. Error z value Pr(>|z|)
(Intercept)       6.556 601950.324       0        1
Sepal.Length     -9.879 194223.245       0        1
Sepal.Width      -7.418  92924.451       0        1
Petal.Length     19.054 144515.981       0        1
Petal.Width      25.033 216058.936       0        1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 1.3863e+02  on 99  degrees of freedom
Residual deviance: 1.3166e-09  on 95  degrees of freedom
AIC: 10

Number of Fisher Scoring iterations: 25

모형의 성능평가(holdout cross validation)

모형의 성능을 평가하기 위해 데이터를 특정비율로 나누어 train 데이터만을 사용하여 모형을 학습시키고 test데이터로 이를 검증하는 방법을 cross validation이라고 한다. cross validation을 통해 모형의 성능을 비교가능 하다.

cross validation에는 holdout cross validation, k-fold cross validation, leave one out cross validation 등이 있다.

data2=data
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_glm<-glm(Species~.,data=train,family=binomial)
#test데이터를 Species_glm 모형으로 분류
pred=predict(Species_glm,test,type='response')

class=as.factor(round(pred))
levels(class)=c('setosa','versicolor')

table(class,test$Species)
            
class        setosa versicolor
  setosa         11          0
  versicolor      0         19

위와 같은 표를 정오분류 표라고 한다.

  • True Positive(TP) : 실제 True인 정답을 True라고 예측 (정답)
  • False Positive(FP) : 실제 False인 정답을 True라고 예측 (오답)
  • False Negative(FN) : 실제 True인 정답을 False라고 예측 (오답)
  • True Negative(TN) : 실제 False인 정답을 False라고 예측 (정답)

모형 평가 지표

  • 정밀도(Precision) : True라고 분류한 것 중에서 실제 True인 것의 비율 \(\frac{TP}{TP+FP}\)

  • 재현율(Recall) : 실제 True인 것 중에서 모델이 True라고 예측한 것의 비율.민감도(sensitivity)라고 표현하기도 한다. \(\frac{TP}{TP+FN}\)

  • 특이도(Specificity) : 실제 False인 것 중에서 False라고 예측한 것의 비율. \(\frac{TN}{TN+FP}\)

  • ROC(Receiver Operating Characteristics) : x축은 1-특이도, y축은 민감도를 나타낸 그래프로 이 그래프의 아랫 면적을 AUC(Area Under the ROC Curve)라 한다.

  • 정확도(Accuracy) : 제대로 예측한 것의 비율 \(\frac{TP+TN}{TP+TN+FP+FN}\)

  • F1 score : 정밀도와 재현율의 조화평균 \(F1\ score = 2 \times \frac{1}{1/재현율+1/정밀도}\)

일반적으로 정확도를 사용해 모형의 성능을 평가하며, 모형이 만일 편향된 예측을 하는 경우 재현율보다 정밀도가 중요해진다. 하지만 정확도는 이를 고려할 수 없다. 이에 자료가 불균형 구조를 띌 때에는 F1 score를 모형평가에 사용한다. 이 경우 정확도를 사용하게되면 편향을 고려할 수 없다.

의사결정나무(dicision Tree)

영역을 나누는 것의 기준은 분류모델을 기준으로 불순도(impurity)/불확실성(uncertainty)이 최소가 될 수 있도록하는 방향으로 진행된다. 순도나 불확실성의 증감을 두고 정보획득(information gain)으로 표현하기도 함.

library(rpart)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_tree<-rpart(Species~.,data=train)
pred=predict(Species_tree,test,type='class')
table(pred,test$Species)
            
pred         setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         3
  virginica       0          0        14

의사결정트리 시각화

library(rattle)
fancyRpartPlot(Species_tree)

서포트 벡터 머신(support vector machine)

가장 가까운 각 변수의 데이터 점들 간의 거리를 최대로 하는 초평면을 선택해 분류하는 기법

library(e1071)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_svm<-svm(Species~.,data=train)
pred=predict(Species_svm,test,type='class')
table(pred,test$Species)
            
pred         setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         2
  virginica       0          0        15

naiveBayes

베이즈정리를 기반으로 만들어젔으며, 확률적으로 독립이라는 가정을 요구한다. 적은 데이터에 대하여도 우수한 성능을 보여준다.

library(e1071)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_nb<-naiveBayes(Species~.,data=train)
pred=predict(Species_nb,test,type='class')
table(pred,test$Species)
            
pred         setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         2
  virginica       0          0        15

앙상블 모형(angsangble)

여러 개의 분류 모형의 결과를 종합하여 분류의 정확도를 높이는 방법

배깅(bootstrap aggregating) : 복원추출을 통해 각각의 모형을 생성하고 그 결과를 종합하는 방법. 부스팅(boosting) : 앞의 모델에서 오분류된 데이터에 대하여 가중치를 주어 표본을 추출하여 모형을 생성하는 작업을 반복하는 방법

트리기반 모델들이므로 control 옵션을 통해 조절 가능하다.

ex)

my.control<-rpart.control(xval=0,cp=0,maxdepth=1)

bagging(Species~.,data=train,control=my.control)

library(adabag)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_bagging<-bagging(Species~.,data=train)
importanceplot(Species_bagging,cex.name=0.8,horiz=TRUE)

pred=predict(Species_bagging,test)
table(pred$class,test$Species)
            
             setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         3
  virginica       0          0        14
library(adabag)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_boosting<-boosting(Species~.,data=train)
importanceplot(Species_boosting,cex.name=0.8,horiz=TRUE)

pred=predict(Species_boosting,test)
table(pred$class,test$Species)
            
             setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         2
  virginica       0          0        15

Random forest

randomforest는 의사결정나무의 앙상블 모형으로 다수의 의사결정나무의 분류 중 최고로 많이 분류된 기준을 분류 예측값으로 제시하며, 회귀의 경우 의사결정나무의 예측값의 평균을 예측값으로 사용.

library(randomForest)
data2=iris
set.seed(1)
idx=sample(1:nrow(data2),nrow(data2)*0.7)
train=data2[idx,]
test=data2[-idx,]

Species_rf<-randomForest(Species~.,data=train)
pred=predict(Species_rf,test,type='class')
table(pred,test$Species)
            
pred         setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         13         2
  virginica       0          0        15

hyper parameter

  • ntree는 앙상블을 할 의사결정나무 개수
  • mrty는 앞서 설명드렸던 것처럼 변수의 부분집합을 만들 때 샘플링하는 변수 개수

하이퍼파라메터 : 알고리즘을 최적화 하는데 사용되는 모수

하이퍼 파라메터의 튜닝 방법은 다양하나 일반적으로 cross validation을 활용한다. cross validation은

seed=3
set.seed(seed)
idx=sample(1:nrow(iris),nrow(iris)*.5)
train=iris[idx,]
set.seed(seed)
idx2=sample(1:nrow(iris[-idx,]),nrow(iris[-idx,])*.5)
valid=iris[-idx,][idx2,]
test=iris[-idx,][-idx2,]
summary(train)
  Sepal.Length    Sepal.Width    Petal.Length    Petal.Width          Species  
 Min.   :4.400   Min.   :2.00   Min.   :1.000   Min.   :0.100   setosa    :27  
 1st Qu.:5.100   1st Qu.:2.80   1st Qu.:1.600   1st Qu.:0.250   versicolor:25  
 Median :5.800   Median :3.00   Median :4.100   Median :1.300   virginica :23  
 Mean   :5.873   Mean   :3.08   Mean   :3.663   Mean   :1.136                  
 3rd Qu.:6.400   3rd Qu.:3.40   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.10   Max.   :6.900   Max.   :2.500                  
summary(valid)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.200   Min.   :1.100   Min.   :0.100   setosa    :13  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.500   1st Qu.:0.400   versicolor:14  
 Median :5.700   Median :3.000   Median :4.500   Median :1.300   virginica :10  
 Mean   :5.768   Mean   :3.116   Mean   :3.659   Mean   :1.154                  
 3rd Qu.:6.500   3rd Qu.:3.400   3rd Qu.:5.000   3rd Qu.:1.700                  
 Max.   :7.400   Max.   :4.400   Max.   :6.100   Max.   :2.500                  
rf.model=randomForest(data=train,Species~.)
set.seed(seed)
trf=tuneRF(valid[,1:4],valid[,5])
mtry = 2  OOB error = 8.11% 
Searching left ...
mtry = 1    OOB error = 13.51% 
-0.6666667 0.05 
Searching right ...
mtry = 4    OOB error = 10.81% 
-0.3333333 0.05 

rf.model=randomForest(data=train,Species~.,mtry=2)
plot(rf.model)

pred=predict(rf.model,test)
table(test$Species,pred)
            pred
             setosa versicolor virginica
  setosa         10          0         0
  versicolor      0         11         0
  virginica       0          0        17
varImpPlot(rf.model)

예측모형 실습(prediction)

학습과 검증방법은 분류모형과 동일하나 평가지표가 수치형이므로 서로 상이하다.

예측모형 평가지표

예측모형도 앞에서와 같이 간단하게 생성할 수 있다. 모형 평가에 사용되는 지표는 분류모형과 달리 값이 연속형 수치이므로 다른 지표들을 사용한다. 일반적으로 RMSE와 MAE를 많이 사용한다.

  • MSE(mean squared error) : \(\frac{1}{n} \sum(Y_i-\hat{Y})^2\)
  • RMSE(root mean squared error) : MSE의 제곱근
  • MAE(mean squared error) : \(\frac{1}{n} \sum|Y_i-\hat{Y}|\)
  • MAPE(mean absolute percentage error) : 오차를 실제값으로 나눈 값들의 합을 n으로 나눈 값 \(\frac{100}{n}\sum|\frac{Y_i-\hat{Y}}{Y_i}|\)
  • BIAS : \(\sum\frac{\hat{Y}-Y_i}{n}\)
  • rBIAS(Relative BIAS) : \(\sum\frac{\hat{Y}-Y_i}{n\times mean(Y_i)}\)
  • rMSEP(Relative Mean Separation) : \(\sum(\hat{Y}-Y_i)/\sum(mean(\hat{Y})-Y_i)\)

bias-variance trade off

mse는 합을 하기전에 제곱을 하였으므로 실제값과 예측값의 차이가 클수록 값이 mae에 비해 커진다. 따라서 필요에 따라 모형지표를 선택할 필요가 있다. 또한 RMSE나 MAE는 단위에 영향을 받는 반면 MAPE는 단위의 영향을 받지 않는다.


예측모형 실습

library(spTimer)
seed=3
set.seed(seed)
idx=sample(1:nrow(iris),nrow(iris)*.5)
train=iris[idx,]
set.seed(seed)
idx2=sample(1:nrow(iris[-idx,]),nrow(iris[-idx,])*.5)
valid=iris[-idx,][idx2,]
test=iris[-idx,][-idx2,]
summary(train)
  Sepal.Length    Sepal.Width    Petal.Length    Petal.Width          Species  
 Min.   :4.400   Min.   :2.00   Min.   :1.000   Min.   :0.100   setosa    :27  
 1st Qu.:5.100   1st Qu.:2.80   1st Qu.:1.600   1st Qu.:0.250   versicolor:25  
 Median :5.800   Median :3.00   Median :4.100   Median :1.300   virginica :23  
 Mean   :5.873   Mean   :3.08   Mean   :3.663   Mean   :1.136                  
 3rd Qu.:6.400   3rd Qu.:3.40   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.10   Max.   :6.900   Max.   :2.500                  
summary(valid)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.200   Min.   :1.100   Min.   :0.100   setosa    :13  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.500   1st Qu.:0.400   versicolor:14  
 Median :5.700   Median :3.000   Median :4.500   Median :1.300   virginica :10  
 Mean   :5.768   Mean   :3.116   Mean   :3.659   Mean   :1.154                  
 3rd Qu.:6.500   3rd Qu.:3.400   3rd Qu.:5.000   3rd Qu.:1.700                  
 Max.   :7.400   Max.   :4.400   Max.   :6.100   Max.   :2.500                  
rf.model=randomForest(data=train,Species~.)
set.seed(seed)
trf=tuneRF(valid[,1:4],valid[,5])
mtry = 2  OOB error = 8.11% 
Searching left ...
mtry = 1    OOB error = 13.51% 
-0.6666667 0.05 
Searching right ...
mtry = 4    OOB error = 10.81% 
-0.3333333 0.05 

rf.model=randomForest(data=train,Sepal.Length~.,mtry=which.min(trf[,2]))
plot(rf.model)

pred=predict(rf.model,test)
spTimer::spT.validation(test$Sepal.Length,pred)
   MSE   RMSE    MAE   MAPE   BIAS  rBIAS  rMSEP 
0.1373 0.3705 0.2702 4.7305 0.1172 0.0200 0.2196 
varImpPlot(rf.model)

참고사이트 참고사이트2

LS0tCnRpdGxlOiAiQUkg7JmAIE1hY2hpbmUgTGVhcm5pbmfsnYQg7JyE7ZWcIOu5heuNsOydtO2EsCDsi6TsirUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0aGVtZTogc3BhY2VsYWIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCioqKioKCiMg66qp7LCoCgorIFsyMDE5LTA3LTE3ICjsmKTsoIQpIDog642w7J207YSw66eI7J2064udIOqwnOyalOyZgCBSIOq4sOy0iCDsgqzsmqnrspVdKGh0dHA6Ly9ycHVicy5jb20vcWtkcms3Nzc3NzcvNTExOTQyKQorIFsyMDE5LTA3LTE3ICjsmKTtm4QpIDog6riw7LSIIO2GteqzhOu2hOyEnV0oaHR0cDovL3JwdWJzLmNvbS9xa2Ryazc3Nzc3Ny81MTE5NDEpCisgKipbMjAxOS0wNy0xOCAo7Jik7KCEKSA6IOygle2YleuNsOydtO2EsOuniOydtOuLnSAtIOyngOuPhO2VmeyKtV0oaHR0cDovL3JwdWJzLmNvbS9xa2Ryazc3Nzc3Ny81MTE5NDApKioKKyBbMjAxOS0wNy0xOCAo7Jik7ZuEKSA6IOygle2YleuNsOydtO2EsOuniOydtOuLnSAtIOu5hOyngOuPhO2VmeyKtV0oaHR0cDovL3JwdWJzLmNvbS9xa2Ryazc3Nzc3Ny81MTE5MzgpCisgWzIwMTktMDctMTkgKOyYpOyghCkgOiDruYTsoJXtmJXrjbDsnbTthLDrp4jsnbTri50t7J6Q66OMIOuLpOujqOq4sF0oaHR0cDovL3JwdWJzLmNvbS9xa2Ryazc3Nzc3Ny81MTE5MzcpCisgWzIwMTktMDctMTkgKOyYpO2bhCkgOiDruYTsoJXtmJXrjbDsnbTthLDrp4jsnbTri50t7YWN7Iqk7Yq466eI7J2064udXShodHRwOi8vcnB1YnMuY29tL3FrZHJrNzc3Nzc3LzUxMTE5OCkKCioqKgoKIyDsoJXtmJUg642w7J207YSwIOuniOydtOuLnQoK7KCV7ZiVLCDruYTsoJXtmJUg642w7J207YSw7J2YIOygleydmCAKCisgKirsoJXtmJXrjbDsnbTthLAgOiDtmJXtg5zqsIAg7J6I6rOgIOyXsOyCsOydtCDqsIDriqXtlZwg642w7J207YSwKioKKyDrsJjsoJXtmJUg642w7J207YSwIDog7ZiV7YOc6rCAIOyeiOycvOuCmCDsl7DsgrDsnbQg67aI6rCA64ql7ZWcIOuNsOydtO2EsAorIOu5hOygle2YleuNsOydtO2EsCA6IO2Yle2DnOqwgCDsl4bqs6AsIOyXsOyCsOydtCDrtojqsIDriqXtlZwg642w7J207YSwCgoKIyMjIOuNsOydtO2EsOuniOydtOuLnQoK7JWe7JeQ7IScIOyWuOq4iSDtlZjsmIDrk68g642w7J207YSw66eI7J2064ud7J2AIOqxsOuMgO2VnCDslpHsnZgg642w7J207YSwIOyGjeyXkCDsib3qsowg65Oc65+s64KY7KeAIOyViuuKlCAqKu2GteqzhOyggSDqt5zsuZkqKuydtOuCmCAqKu2MqO2EtCoq6rO8IOqwmeydgCAqKuycoOyaqe2VnCDsoJXrs7Trpbwg7LC+7JWE64K064qUIOqzvOyglSoq7J206528IOunkO2VoCDsiJgg7J6I64ukLiDrjbDsnbTthLAg64K07JeQIOygleuztOulvCDssL7ripQg6rKD7J20IOyjvCDrqqnsoIEKCgojIyDrjbDsnbTthLDrp4jsnbTri53snZgg7KKF66WYCgorIOu2hOulmCA6IOyDiOuhreqyjCDrgpjtg4DrgpjripQg7ZiE7IOB7J2EIOq4sOyhtCDsoJXsnZjrkJwg7KeR7ZWp7JeQIOuwsOygle2VmOuKlCDqsoPsnYQg7J2Y66+4Cisg7LaU7KCVIDog7IiY7J6FLCDsiJjspIAsIOyLoOyaqeyelOqzoOyymOufvCDsl7Dsho3rkJwg67OA7IiY7J2YIOqwkuydhCDstpTsoJXtlZjripQg6rKD7J2EIOydmOuvuAorIOyYiOy4oSA6IOuvuOuemOydmCDqsJLsnYQg7LaU7KCV7ZWY6rGw64KYIOu2hOulmO2VmOuKlCDqsoPsnYQg7J2Y66+4Cisg6rWw7KeRIDog7J207KeI7KCB7J24IOuqqOynkeuLqOydhCDrj5nsp4jshLHsnYQg7KeA64uMIOq3uOujueuzhOuhnCDshLjrtoTtmZQg7ZWY64qUIOqyg+ydhCDsnZjrr7gKKyDsl7DqtIAgOiDqsJnsnbQg7YyU66as64qUIOusvOqxtOuTpCDsgqzsnbTsnZgg7Jew6rSA7ISx7J2EIO2MjOyVhe2VmOuKlCDrtoTshJ0KCioqKgoKIyMgaXJpcyBkYXRhIOyEpOuqhQoKaXJpcyDsnpDro4zripQgUuyXkOyEnCDquLDrs7jsnLzroZwg7KCc6rO17ZWY64qUIOu2k+q9g+yXkCDrjIDtlZwg7J6Q66OMCgoqKuuzgOyImCoqCgorIFNlcGFsLkxlbmd0aCA6IOq9g+uwm+y5qOydmCDquLjsnbTsoJXrs7QKKyBTZXBhbC5XaWR0aCA6IOq9g+uwm+y5qOydmCDrhIjruYTsoJXrs7QKKyBQZXRhbC5MZW5ndGggOiDqvYPsno7snZgg6ri47J207KCV67O0CisgUGV0YWwuV2lkdGggOiDqvYPsno7snZgg64SI67mE7KCV67O0CisgU3BlY2llcyA6IOq9g+ydmCDsooXrpZgoc2V0b3NhLCB2ZXJzaWNvbG9yLCB2aXJnaW5pY2EpCgpzZXRvc2HsmYAgdmVyc2ljb2xvcuulvCDrtoTrpZjtlZjripQg66qo7ZiV7J2EIOunjOuTpOugpOqzoCDtlZzri6QuIOydtOyXkCDrlLDrnbwg7ZWE7JqU7ZWcIOuNsOydtO2EsOulvCDstpTstpwKCmBgYHtyfQoKZGF0YShpcmlzKQpkYXRhPWlyaXMKc3VtbWFyeShkYXRhKQoj7IOB7J6Q6re466a8IOyDneyEsQpib3hwbG90KGlyaXNbLC01XSkKCmBgYAojIyDrtoTrpZgg66qo7ZiVIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoK6riw7KG0IOyekOujjOyXkCDrjIDtlbQg7ZWZ7Iq17ZWcIOuqqO2YleydhCDrsJTtg5XsnLzroZwg7IOI66Gt6rKMIOuCmO2DgOuCmOuKlCDsnpDro4zsl5Ag64yA7ZWY7JesIOygleydmOuQnCDsp5Htlansl5Ag67Cw7KCV7ZWY64qUIOqygyDrqqjtmJXsnYQg7J2Y66+47ZWc64ukLgoK642w7J207YSwIOygleyerCAtIOuqqO2YlSDsg53shLEgLSDrqqjtmJXtj4nqsIAg67CPIOuqqO2Yleu5hOq1kCDsiJzsnLzroZwg7KeE7ZaJCgoKIyMjIOuhnOyngOyKpO2LsSDtmozqt4DrqqjtmJUobG9naXN0aWMgbW9kZWwpCgrroZzsp4DsiqTti7Eg7ZqM6reA64qUIOu2hOyEnSDrjIDsg4Hrk6TsnbQg7Jes65+sIOynkeuLqOycvOuhnCDrgpjriITslrTsp4Qg6rK97JqwLCDrj4Xrpr0g67OA7IiY7J2YIOyEoO2YlSDqsrDtlansnYQg7J207Jqp7ZWY7JesIOqwnOuzhCDqtIDsuKHsuZjqsIAg7Ja064qQIOynkeuLqOyXkCDsho3tlZjripTsp4Ag7ZmV66Wg7J2EIOqzhOyCsO2VmOuKlCDrtoTrpZgg6riw67KV7J2064ukLiDroZzsp4DsiqTti7HsnbTrnoAgb2Rkc+yXkCBsb2frpbwg7Leo7ZWcIOqwkuycvOuhnCDroZzsp4DsiqTti7Eg7ZqM6reA66qo7ZiV7J2AIOyVhOuemOyZgCDqsJnsnYAg7IiY7Iud7Jy866GcIOuCmO2DgOuCnOuLpC4KCiRcbG4oXGZyYWN7UH17MS1QfSk9YStiWCQKCiRvZGRzID1cZnJhY3vslrTrlqTsnbzsnbRcIOydvOyWtOuCoFwg7ZmV66WgfXvslrTrlqRcIOydvOydtFwg7J287Ja064KY7KeAXCDslYrsnYRcIO2ZleuloH0kCgoKYGBge3IsIGVjaG89RkFMU0V9CiNpcmlzIOyekOujjOydmCBTcGVjaWVzIOqwgCAnc2V0b3NhJywndmVyc2ljb2xvcifsnbgg6rK97Jqw66eMIOy2lOy2nApkYXRhPC1zdWJzZXQoZGF0YSxTcGVjaWVzPT0ic2V0b3NhInxTcGVjaWVzPT0idmVyc2ljb2xvciIpCiNmYWN0b3LroZwg67OA7ZmYCmRhdGEkU3BlY2llczwtZmFjdG9yKGRhdGEkU3BlY2llcykKIwp4MT1zZXEobWluKGRhdGEkU2VwYWwuTGVuZ3RoKSxtYXgoZGF0YSRTZXBhbC5MZW5ndGgpLGxlbj0xMDApCnBsb3QoeDEsZGF0YSRTcGVjaWVzLHBjaD0xNixheGVzPUYseWxhYj0iU3BlY2llcyIsY29sPWNvbG9ycygpWzI2Mjo0MTFdLHhsYWI9IlNlcGFsLkxlbmd0aCIsdHlwZT0nbicpCmxpbmVzKHgxLDErMS8oMSsoMS9leHAoLTI3LjgzMSs1LjE0MCp4MSkpKSxsd2Q9Myx0eXBlPSJsIixjb2w9Y29sb3JzKClbMTAwXSkKCmF4aXMoMixhdD1zZXEoMSwyLGxlbj02KSxsYWJlbD1zZXEoMSwyLGxlbj02KSkKYXhpcygxLGF0PXNlcSg0LjUsNyxieT0wLjUpLGxhYmVsPXNlcSg0LjUsNyxieT0wLjUpKQpncmlkKDAsNSkKCmBgYAoK66Gc7KeA7Iqk7Yux7ZqM6reAIOuqqO2YleydhCDsgqzsmqntlZjsl6wgc2V0b3Nh7JmAIHZlcnNpY29sb3Lrpbwg67aE66WYCgpgYGB7cix3YXJuaW5nPUZBTFNFfQoj642w7J207YSwIOy2lOy2nAojaXJpcyDsnpDro4zsnZggU3BlY2llcyDqsIAgJ3NldG9zYScsJ3ZlcnNpY29sb3In7J24IOqyveyasOunjCDstpTstpwKZGF0YTwtc3Vic2V0KGRhdGEsU3BlY2llcz09InNldG9zYSJ8U3BlY2llcz09InZlcnNpY29sb3IiKQojZmFjdG9y66GcIOuzgO2ZmApkYXRhJFNwZWNpZXM8LWZhY3RvcihkYXRhJFNwZWNpZXMpCiPsnbzrsJjtmZTqsIDrspXrqqjtmJXsg53shLEKU3BlY2llc19nbG08LWdsbShTcGVjaWVzfi4sZGF0YT1kYXRhLGZhbWlseT1iaW5vbWlhbCkKIyBTcGVjaWVzX2dsbTwtZ2xtKFNwZWNpZXN+LixkYXRhPWRhdGEsZmFtaWx5PWJpbm9taWFsKQojIFNwZWNpZXNfZ2xtPC1nbG0oU3BlY2llc34uLVNlcGFsLkxlbmd0aCxkYXRhPWRhdGEsZmFtaWx5PWJpbm9taWFsKQojIFNwZWNpZXNfZ2xtPC1nbG0oU3BlY2llc34uK0koU2VwYWwuTGVuZ3RoXjIpLGRhdGE9ZGF0YSxmYW1pbHk9Ymlub21pYWwpCgoj66qo7ZiVIOyalOyVvQpzdW1tYXJ5KFNwZWNpZXNfZ2xtKQpgYGAKCiMjIyDrqqjtmJXsnZgg7ISx64ql7Y+J6rCAKGhvbGRvdXQgY3Jvc3MgdmFsaWRhdGlvbikKCuuqqO2YleydmCDshLHriqXsnYQg7Y+J6rCA7ZWY6riwIOychO2VtCDrjbDsnbTthLDrpbwg7Yq57KCV67mE7Jyo66GcIOuCmOuIhOyWtCB0cmFpbiDrjbDsnbTthLDrp4zsnYQg7IKs7Jqp7ZWY7JesIOuqqO2YleydhCDtlZnsirXsi5ztgqTqs6AgdGVzdOuNsOydtO2EsOuhnCDsnbTrpbwg6rKA7Kad7ZWY64qUIOuwqeuyleydhCBjcm9zcyB2YWxpZGF0aW9u7J2065286rOgIO2VnOuLpC4gY3Jvc3MgdmFsaWRhdGlvbuydhCDthrXtlbQg66qo7ZiV7J2YIOyEseuKpeydhCDruYTqtZDqsIDriqUg7ZWY64ukLiAKCipjcm9zcyB2YWxpZGF0aW9u7JeQ64qUIGhvbGRvdXQgY3Jvc3MgdmFsaWRhdGlvbiwgay1mb2xkIGNyb3NzIHZhbGlkYXRpb24sIGxlYXZlIG9uZSBvdXQgY3Jvc3MgdmFsaWRhdGlvbiDrk7HsnbQg7J6I64ukLioKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmRhdGEyPWRhdGEKc2V0LnNlZWQoMSkKI+yekOujjCA3OjPsnLzroZwg7LaU7LacCmlkeD1zYW1wbGUoMTpucm93KGRhdGEyKSxucm93KGRhdGEyKSowLjcpCnRyYWluPWRhdGEyW2lkeCxdCnRlc3Q9ZGF0YTJbLWlkeCxdCiProZzsp4DsiqTti7Htmozqt4DrqqjtmJUg7IOd7ISxClNwZWNpZXNfZ2xtPC1nbG0oU3BlY2llc34uLGRhdGE9dHJhaW4sZmFtaWx5PWJpbm9taWFsKQojdGVzdOuNsOydtO2EsOulvCBTcGVjaWVzX2dsbSDrqqjtmJXsnLzroZwg67aE66WYCnByZWQ9cHJlZGljdChTcGVjaWVzX2dsbSx0ZXN0LHR5cGU9J3Jlc3BvbnNlJykKI+u2hOulmO2VmOq4sOychO2VtCBmYWN0b3LroZwg67OA6rK9IApjbGFzcz1hcy5mYWN0b3Iocm91bmQocHJlZCkpCmxldmVscyhjbGFzcyk9Yygnc2V0b3NhJywndmVyc2ljb2xvcicpCgp0YWJsZShjbGFzcyx0ZXN0JFNwZWNpZXMpCmBgYAoK7JyE7JmAIOqwmeydgCDtkZzrpbwg7KCV7Jik67aE66WYIO2RnOudvOqzoCDtlZzri6QuCgohW10oL2Nsb3VkL3Byb2plY3Qv7Lqh7LKYMTMuUE5HKQoKKyBUcnVlIFBvc2l0aXZlKFRQKSA6IOyLpOygnCBUcnVl7J24IOygleuLteydhCBUcnVl65286rOgIOyYiOy4oSAo7KCV64u1KQorIEZhbHNlIFBvc2l0aXZlKEZQKSA6IOyLpOygnCBGYWxzZeyduCDsoJXri7XsnYQgVHJ1ZeudvOqzoCDsmIjsuKEgKOyYpOuLtSkKKyBGYWxzZSBOZWdhdGl2ZShGTikgOiDsi6TsoJwgVHJ1ZeyduCDsoJXri7XsnYQgRmFsc2Xrnbzqs6Ag7JiI7LihICjsmKTri7UpCisgVHJ1ZSBOZWdhdGl2ZShUTikgOiDsi6TsoJwgRmFsc2Xsnbgg7KCV64u17J2EIEZhbHNl65286rOgIOyYiOy4oSAo7KCV64u1KQoKKirrqqjtmJUg7Y+J6rCAIOyngO2RnCoqCgorIOygleuwgOuPhChQcmVjaXNpb24pIDogVHJ1ZeudvOqzoCDrtoTrpZjtlZwg6rKDIOykkeyXkOyEnCDsi6TsoJwgVHJ1ZeyduCDqsoPsnZgg67mE7JyoCiRcZnJhY3tUUH17VFArRlB9JAoKKyDsnqztmITsnKgoUmVjYWxsKSA6IOyLpOygnCBUcnVl7J24IOqygyDspJHsl5DshJwg66qo64247J20IFRydWXrnbzqs6Ag7JiI7Lih7ZWcIOqyg+ydmCDruYTsnKgu66+86rCQ64+EKHNlbnNpdGl2aXR5KeudvOqzoCDtkZztmITtlZjquLDrj4Qg7ZWc64ukLgokXGZyYWN7VFB9e1RQK0ZOfSQKCisg7Yq57J2064+EKFNwZWNpZmljaXR5KSA6IOyLpOygnCBGYWxzZeyduCDqsoMg7KSR7JeQ7IScIEZhbHNl65286rOgIOyYiOy4oe2VnCDqsoPsnZgg67mE7JyoLgokXGZyYWN7VE59e1ROK0ZQfSQKCisgUk9DKFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpY3MpIDogeOy2leydgCAxLe2KueydtOuPhCwgeey2leydgCDrr7zqsJDrj4Trpbwg64KY7YOA64K4IOq3uOuemO2UhOuhnCDsnbQg6re4656Y7ZSE7J2YIOyVhOueqyDrqbTsoIHsnYQgQVVDKEFyZWEgVW5kZXIgdGhlIFJPQyBDdXJ2ZSnrnbwg7ZWc64ukLgoKKyDsoJXtmZXrj4QoQWNjdXJhY3kpIDog7KCc64yA66GcIOyYiOy4oe2VnCDqsoPsnZgg67mE7JyoCiRcZnJhY3tUUCtUTn17VFArVE4rRlArRk59JAoKKyBGMSBzY29yZSA6IOygleuwgOuPhOyZgCDsnqztmITsnKjsnZgg7KGw7ZmU7Y+J6regCiRGMVwgc2NvcmUgPSAyIFx0aW1lcyBcZnJhY3sxfXsxL+yerO2YhOycqCsxL+ygleuwgOuPhH0kCgoKCioq7J2867CY7KCBKirsnLzroZwgKirsoJXtmZXrj4QqKuulvCDsgqzsmqntlbQgKirrqqjtmJXsnZgg7ISx64ql7J2EIO2PieqwgCoq7ZWY66mwLCDrqqjtmJXsnbQg66eM7J28IO2OuO2WpeuQnCDsmIjsuKHsnYQg7ZWY64qUIOqyveyasCDsnqztmITsnKjrs7Tri6Qg7KCV67CA64+E6rCAIOykkeyalO2VtOynhOuLpC4g7ZWY7KeA66eMIOygle2ZleuPhOuKlCDsnbTrpbwg6rOg66Ck7ZWgIOyImCDsl4bri6QuIOydtOyXkCAqKuyekOujjOqwgCDrtojqt6DtmJUg6rWs7KGw66W8IOudjCDrlYwqKuyXkOuKlCAqKkYxIHNjb3JlKirrpbwg66qo7ZiV7Y+J6rCA7JeQIOyCrOyaqe2VnOuLpC4g7J20IOqyveyasCDsoJXtmZXrj4Trpbwg7IKs7Jqp7ZWY6rKM65CY66m0IO2OuO2WpeydhCDqs6DroKTtlaAg7IiYIOyXhuuLpC4KCiMjIyDsnZjsgqzqsrDsoJXrgpjrrLQoZGljaXNpb24gVHJlZSkKCuyYgeyXreydhCDrgpjriITripQg6rKD7J2YIOq4sOykgOydgCDrtoTrpZjrqqjrjbjsnYQg6riw7KSA7Jy866GcIOu2iOyInOuPhChpbXB1cml0eSkv67aI7ZmV7Iuk7ISxKHVuY2VydGFpbnR5KeydtCDstZzshozqsIAg65CgIOyImCDsnojrj4TroZ3tlZjripQg67Cp7Zal7Jy866GcIOynhO2WieuQnOuLpC4g7Iic64+E64KYIOu2iO2ZleyLpOyEseydmCDspp3qsJDsnYQg65GQ6rOgIOygleuztO2ajeuTnShpbmZvcm1hdGlvbiBnYWluKeycvOuhnCDtkZztmITtlZjquLDrj4Qg7ZWoLgoKYGBge3J9CiPsnZjsgqzqsrDsoJXrgpjrrLTsg53shLHsnYQg7JyE7ZWcIO2MqO2CpOyngApsaWJyYXJ5KHJwYXJ0KQoKZGF0YTI9aXJpcwpzZXQuc2VlZCgxKQoKaWR4PXNhbXBsZSgxOm5yb3coZGF0YTIpLG5yb3coZGF0YTIpKjAuNykKI+yekOujjOulvCA3OjPruYTsnKjroZwg7LaU7LacCnRyYWluPWRhdGEyW2lkeCxdCnRlc3Q9ZGF0YTJbLWlkeCxdCiPsnZjsgqzqsrDsoJXrgpjrrLTrqqjtmJUg7IOd7ISxClNwZWNpZXNfdHJlZTwtcnBhcnQoU3BlY2llc34uLGRhdGE9dHJhaW4pCiN0ZXN0642w7J207YSw66W8IOydmOyCrOqysOygleuCmOustOuqqO2YleycvOuhnCDrtoTrpZgKcHJlZD1wcmVkaWN0KFNwZWNpZXNfdHJlZSx0ZXN0LHR5cGU9J2NsYXNzJykKCnRhYmxlKHByZWQsdGVzdCRTcGVjaWVzKQpgYGAKCuydmOyCrOqysOygle2KuOumrCDsi5zqsIHtmZQKCmBgYHtyfQpsaWJyYXJ5KHJhdHRsZSkKI+2KuOumrOuqqO2YlSDsi5zqsIHtmZQKZmFuY3lScGFydFBsb3QoU3BlY2llc190cmVlKQoKYGBgCgojIyMg7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Aoc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSkKCuqwgOyepSDqsIDquYzsmrQg6rCBIOuzgOyImOydmCDrjbDsnbTthLAg7KCQ65OkIOqwhOydmCDqsbDrpqzrpbwg7LWc64yA66GcIO2VmOuKlCDstIjtj4nrqbTsnYQg7ISg7YOd7ZW0IOu2hOulmO2VmOuKlCDquLDrspUKCiFbXSgvY2xvdWQvcHJvamVjdC/suqHsspgxNC5QTkcpCgoKYGBge3J9CiPrqLjsi6Drn6zri50g6rSA66Co7Yyo7YKk7KeACmxpYnJhcnkoZTEwNzEpCmRhdGEyPWlyaXMKc2V0LnNlZWQoMSkKaWR4PXNhbXBsZSgxOm5yb3coZGF0YTIpLG5yb3coZGF0YTIpKjAuNykKdHJhaW49ZGF0YTJbaWR4LF0KdGVzdD1kYXRhMlstaWR4LF0KI3N2bSDrqqjtmJXsg53shLEKU3BlY2llc19zdm08LXN2bShTcGVjaWVzfi4sZGF0YT10cmFpbikKcHJlZD1wcmVkaWN0KFNwZWNpZXNfc3ZtLHRlc3QsdHlwZT0nY2xhc3MnKQp0YWJsZShwcmVkLHRlc3QkU3BlY2llcykKCmBgYAoKCiMjIyBuYWl2ZUJheWVzCgrrsqDsnbTspojsoJXrpqzrpbwg6riw67CY7Jy866GcIOunjOuTpOyWtOyglOycvOupsCwg7ZmV66Wg7KCB7Jy866GcIOuPheumveydtOudvOuKlCDqsIDsoJXsnYQg7JqU6rWs7ZWc64ukLiDsoIHsnYAg642w7J207YSw7JeQIOuMgO2VmOyXrOuPhCDsmrDsiJjtlZwg7ISx64ql7J2EIOuztOyXrOykgOuLpC4KCgpgYGB7cn0KbGlicmFyeShlMTA3MSkKZGF0YTI9aXJpcwpzZXQuc2VlZCgxKQppZHg9c2FtcGxlKDE6bnJvdyhkYXRhMiksbnJvdyhkYXRhMikqMC43KQp0cmFpbj1kYXRhMltpZHgsXQp0ZXN0PWRhdGEyWy1pZHgsXQoj64KY7J2067iM67Kg7J207KaIIOuqqO2YleyDneyEsQpTcGVjaWVzX25iPC1uYWl2ZUJheWVzKFNwZWNpZXN+LixkYXRhPXRyYWluKQpwcmVkPXByZWRpY3QoU3BlY2llc19uYix0ZXN0LHR5cGU9J2NsYXNzJykKdGFibGUocHJlZCx0ZXN0JFNwZWNpZXMpCgpgYGAKCiMjIyDslZnsg4HruJQg66qo7ZiVKGFuZ3NhbmdibGUpCgrsl6zrn6wg6rCc7J2YIOu2hOulmCDrqqjtmJXsnZgg6rKw6rO866W8IOyihe2Vqe2VmOyXrCDrtoTrpZjsnZgg7KCV7ZmV64+E66W8IOuGkuydtOuKlCDrsKnrspUKCuuwsOq5hShib290c3RyYXAgYWdncmVnYXRpbmcpIDog67O17JuQ7LaU7Lac7J2EIO2Gte2VtCDqsIHqsIHsnZgg66qo7ZiV7J2EIOyDneyEse2VmOqzoCDqt7gg6rKw6rO866W8IOyihe2Vqe2VmOuKlCDrsKnrspUuCuu2gOyKpO2MhShib29zdGluZykgOiDslZ7snZgg66qo64247JeQ7IScIOyYpOu2hOulmOuQnCDrjbDsnbTthLDsl5Ag64yA7ZWY7JesIOqwgOykkey5mOulvCDso7zslrQg7ZGc67O47J2EIOy2lOy2nO2VmOyXrCDrqqjtmJXsnYQg7IOd7ISx7ZWY64qUIOyekeyXheydhCDrsJjrs7XtlZjripQg67Cp67KVCgrtirjrpqzquLDrsJgg66qo642465Ok7J2066+A66GcIGNvbnRyb2wg7Ji17IWY7J2EIO2Gte2VtCDsobDsoIgg6rCA64ql7ZWY64ukLgoKZXgpCgpteS5jb250cm9sPC1ycGFydC5jb250cm9sKHh2YWw9MCxjcD0wLG1heGRlcHRoPTEpCgpiYWdnaW5nKFNwZWNpZXN+LixkYXRhPXRyYWluLGNvbnRyb2w9bXkuY29udHJvbCkKCmBgYHtyfQoj67Cw6rmFIOu2gOyKpO2MhSDqtIDroKgg7Yyo7YKk7KeACmxpYnJhcnkoYWRhYmFnKQpkYXRhMj1pcmlzCnNldC5zZWVkKDEpCmlkeD1zYW1wbGUoMTpucm93KGRhdGEyKSxucm93KGRhdGEyKSowLjcpCnRyYWluPWRhdGEyW2lkeCxdCnRlc3Q9ZGF0YTJbLWlkeCxdCgoj67Cw6rmFClNwZWNpZXNfYmFnZ2luZzwtYmFnZ2luZyhTcGVjaWVzfi4sZGF0YT10cmFpbikKI+uzgOyImOykkeyalOuPhAppbXBvcnRhbmNlcGxvdChTcGVjaWVzX2JhZ2dpbmcsY2V4Lm5hbWU9MC44LGhvcml6PVRSVUUpCnByZWQ9cHJlZGljdChTcGVjaWVzX2JhZ2dpbmcsdGVzdCkKdGFibGUocHJlZCRjbGFzcyx0ZXN0JFNwZWNpZXMpCgpgYGAKCgoKYGBge3J9CmxpYnJhcnkoYWRhYmFnKQpkYXRhMj1pcmlzCnNldC5zZWVkKDEpCmlkeD1zYW1wbGUoMTpucm93KGRhdGEyKSxucm93KGRhdGEyKSowLjcpCnRyYWluPWRhdGEyW2lkeCxdCnRlc3Q9ZGF0YTJbLWlkeCxdCiPrtoDsiqTtjIUKU3BlY2llc19ib29zdGluZzwtYm9vc3RpbmcoU3BlY2llc34uLGRhdGE9dHJhaW4pCiPrs4DsiJjspJHsmpTrj4QKaW1wb3J0YW5jZXBsb3QoU3BlY2llc19ib29zdGluZyxjZXgubmFtZT0wLjgsaG9yaXo9VFJVRSkKcHJlZD1wcmVkaWN0KFNwZWNpZXNfYm9vc3RpbmcsdGVzdCkKdGFibGUocHJlZCRjbGFzcyx0ZXN0JFNwZWNpZXMpCmBgYAoKKipSYW5kb20gZm9yZXN0KioKCnJhbmRvbWZvcmVzdOuKlCDsnZjsgqzqsrDsoJXrgpjrrLTsnZgg7JWZ7IOB67iUIOuqqO2YleycvOuhnCDri6TsiJjsnZgg7J2Y7IKs6rKw7KCV64KY66y07J2YIOu2hOulmCDspJEg7LWc6rOg66GcIOunjuydtCDrtoTrpZjrkJwg6riw7KSA7J2EIOu2hOulmCDsmIjsuKHqsJLsnLzroZwg7KCc7Iuc7ZWY66mwLCDtmozqt4DsnZgg6rK97JqwIOydmOyCrOqysOygleuCmOustOydmCDsmIjsuKHqsJLsnZgg7Y+J6reg7J2EIOyYiOy4oeqwkuycvOuhnCDsgqzsmqkuCgpgYGB7cn0KI+ugjOuNpO2PrOugiOyKpO2KuCDtjKjtgqTsp4AKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmRhdGEyPWlyaXMKc2V0LnNlZWQoMSkKaWR4PXNhbXBsZSgxOm5yb3coZGF0YTIpLG5yb3coZGF0YTIpKjAuNykKdHJhaW49ZGF0YTJbaWR4LF0KdGVzdD1kYXRhMlstaWR4LF0KCiPrqqjtmJXsg53shLEKU3BlY2llc19yZjwtcmFuZG9tRm9yZXN0KFNwZWNpZXN+LixkYXRhPXRyYWluKQpwcmVkPXByZWRpY3QoU3BlY2llc19yZix0ZXN0LHR5cGU9J2NsYXNzJykKdGFibGUocHJlZCx0ZXN0JFNwZWNpZXMpCmBgYAoKKmh5cGVyIHBhcmFtZXRlcioKCisgbnRyZWXripQg7JWZ7IOB67iU7J2EIO2VoCDsnZjsgqzqsrDsoJXrgpjrrLQg6rCc7IiYCisgbXJ0eeuKlCDslZ7shJwg7ISk66qF65Oc66C4642YIOqyg+yymOufvCDrs4DsiJjsnZgg67aA67aE7KeR7ZWp7J2EIOunjOuTpCDrlYwg7IOY7ZSM66eB7ZWY64qUIOuzgOyImCDqsJzsiJgKCgoq7ZWY7J207Y287YyM652866mU7YSwIDog7JWM6rOg66as7KaY7J2EIOy1nOygge2ZlCDtlZjripTrjbAg7IKs7Jqp65CY64qUIOuqqOyImCoKCu2VmOydtO2NvCDtjIzrnbzrqZTthLDsnZgg7Yqc64udIOuwqeuyleydgCDri6TslpHtlZjrgpgg7J2867CY7KCB7Jy866GcIGNyb3NzIHZhbGlkYXRpb27snYQg7Zmc7Jqp7ZWc64ukLiBjcm9zcyB2YWxpZGF0aW9u7J2AIAoKYGBge3J9CiPsnpDro4zrpbwgdHJhaW4sIHRlc3QsIHZhbGlkYWlvbuuhnCAgNToyLjU6Mi41IOu5hOycqOuhnCDsnpDro4wg7IOd7ISxCnNlZWQ9MwpzZXQuc2VlZChzZWVkKQppZHg9c2FtcGxlKDE6bnJvdyhpcmlzKSxucm93KGlyaXMpKi41KQp0cmFpbj1pcmlzW2lkeCxdCnNldC5zZWVkKHNlZWQpCmlkeDI9c2FtcGxlKDE6bnJvdyhpcmlzWy1pZHgsXSksbnJvdyhpcmlzWy1pZHgsXSkqLjUpCnZhbGlkPWlyaXNbLWlkeCxdW2lkeDIsXQp0ZXN0PWlyaXNbLWlkeCxdWy1pZHgyLF0Kc3VtbWFyeSh0cmFpbikKc3VtbWFyeSh2YWxpZCkKI+uqqO2YleyDneyEsQpyZi5tb2RlbD1yYW5kb21Gb3Jlc3QoZGF0YT10cmFpbixTcGVjaWVzfi4pCnNldC5zZWVkKHNlZWQpCiPtlZjsnbTtjbztjIzrnbzrqZTthLAg7LWc7KCB7ZmUCnRyZj10dW5lUkYodmFsaWRbLDE6NF0sdmFsaWRbLDVdKQoj66qo7ZiV7IOd7ISxCnJmLm1vZGVsPXJhbmRvbUZvcmVzdChkYXRhPXRyYWluLFNwZWNpZXN+LixtdHJ5PTIpCiNudHJlZSDshKTsoJUKcGxvdChyZi5tb2RlbCkKcHJlZD1wcmVkaWN0KHJmLm1vZGVsLHRlc3QpCnRhYmxlKHRlc3QkU3BlY2llcyxwcmVkKQoj67OA7IiY7KSR7JqU64+EIAp2YXJJbXBQbG90KHJmLm1vZGVsKQpgYGAKCgojIyMg7JiI7Lih66qo7ZiVIOyLpOyKtShwcmVkaWN0aW9uKQoK7ZWZ7Iq16rO8IOqygOymneuwqeuyleydgCDrtoTrpZjrqqjtmJXqs7wg64+Z7J287ZWY64KYIO2PieqwgOyngO2RnOqwgCDsiJjsuZjtmJXsnbTrr4DroZwg7ISc66GcIOyDgeydtO2VmOuLpC4gCgojIyMjIOyYiOy4oeuqqO2YlSDtj4nqsIDsp4DtkZwKCuyYiOy4oeuqqO2YleuPhCDslZ7sl5DshJzsmYAg6rCZ7J20IOqwhOuLqO2VmOqyjCDsg53shLHtlaAg7IiYIOyeiOuLpC4g66qo7ZiVIO2PieqwgOyXkCDsgqzsmqnrkJjripQg7KeA7ZGc64qUIOu2hOulmOuqqO2YleqzvCDri6zrpqwg6rCS7J20IOyXsOyGje2YlSDsiJjsuZjsnbTrr4DroZwg64uk66W4IOyngO2RnOuTpOydhCDsgqzsmqntlZzri6QuIOydvOuwmOyggeycvOuhnCBSTVNF7JmAIE1BReulvCDrp47snbQg7IKs7Jqp7ZWc64ukLiAKCisgTVNFKG1lYW4gc3F1YXJlZCBlcnJvcikgOiAkXGZyYWN7MX17bn0gXHN1bShZX2ktXGhhdHtZfSleMiQKKyBSTVNFKHJvb3QgbWVhbiBzcXVhcmVkIGVycm9yKSA6IE1TReydmCDsoJzqs7Hqt7wKKyBNQUUobWVhbiBzcXVhcmVkIGVycm9yKSA6ICRcZnJhY3sxfXtufSBcc3VtfFlfaS1caGF0e1l9fCQKKyBNQVBFKG1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBlcnJvcikgOiDsmKTssKjrpbwg7Iuk7KCc6rCS7Jy866GcIOuCmOuIiCDqsJLrk6TsnZgg7ZWp7J2EIG7snLzroZwg64KY64iIIOqwkgokXGZyYWN7MTAwfXtufVxzdW18XGZyYWN7WV9pLVxoYXR7WX19e1lfaX18JAorIEJJQVMgOiAkXHN1bVxmcmFje1xoYXR7WX0tWV9pfXtufSQKKyByQklBUyhSZWxhdGl2ZSBCSUFTKSA6ICAkXHN1bVxmcmFje1xoYXR7WX0tWV9pfXtuXHRpbWVzIG1lYW4oWV9pKX0kCisgck1TRVAoUmVsYXRpdmUgTWVhbiBTZXBhcmF0aW9uKSA6IAokXHN1bShcaGF0e1l9LVlfaSkvXHN1bShtZWFuKFxoYXR7WX0pLVlfaSkkCgoqKmJpYXMtdmFyaWFuY2UgdHJhZGUgb2ZmKioKCiFbXSgvY2xvdWQvcHJvamVjdC/suqHsspgxNS5QTkcpCgptc2XripQg7ZWp7J2EIO2VmOq4sOyghOyXkCDsoJzqs7HsnYQg7ZWY7JiA7Jy866+A66GcIOyLpOygnOqwkuqzvCDsmIjsuKHqsJLsnZgg7LCo7J206rCAIO2BtOyImOuhnSDqsJLsnbQgbWFl7JeQIOu5hO2VtCDsu6Tsp4Tri6QuIOuUsOudvOyEnCDtlYTsmpTsl5Ag65Sw6528IOuqqO2YleyngO2RnOulvCDshKDtg53tlaAg7ZWE7JqU6rCAIOyeiOuLpC4g65iQ7ZWcIFJNU0XrgpggTUFF64qUIOuLqOychOyXkCDsmIHtlqXsnYQg67Cb64qUIOuwmOuptCBNQVBF64qUIOuLqOychOydmCDsmIHtlqXsnYQg67Cb7KeAIOyViuuKlOuLpC4KCioqKgoKIyMjIyDsmIjsuKHrqqjtmJUg7Iuk7Iq1IAoKYGBge3J9CiNzcFRpbWVyIO2MqO2CpOyngOyXkCDsnITsl5DshJwg7ISk66qF7ZWcIOyngO2RnOuTpOydhCDsoJzqs7UKbGlicmFyeShzcFRpbWVyKQpzZWVkPTMKc2V0LnNlZWQoc2VlZCkKaWR4PXNhbXBsZSgxOm5yb3coaXJpcyksbnJvdyhpcmlzKSouNSkKdHJhaW49aXJpc1tpZHgsXQpzZXQuc2VlZChzZWVkKQppZHgyPXNhbXBsZSgxOm5yb3coaXJpc1staWR4LF0pLG5yb3coaXJpc1staWR4LF0pKi41KQp2YWxpZD1pcmlzWy1pZHgsXVtpZHgyLF0KdGVzdD1pcmlzWy1pZHgsXVstaWR4MixdCnN1bW1hcnkodHJhaW4pCnN1bW1hcnkodmFsaWQpCiPrqqjtmJXsg53shLEKcmYubW9kZWw9cmFuZG9tRm9yZXN0KGRhdGE9dHJhaW4sU3BlY2llc34uKQpzZXQuc2VlZChzZWVkKQp0cmY9dHVuZVJGKHZhbGlkWywxOjRdLHZhbGlkWyw1XSkKcmYubW9kZWw9cmFuZG9tRm9yZXN0KGRhdGE9dHJhaW4sU2VwYWwuTGVuZ3Rofi4sbXRyeT13aGljaC5taW4odHJmWywyXSkpCnBsb3QocmYubW9kZWwpCgpwcmVkPXByZWRpY3QocmYubW9kZWwsdGVzdCkKCnNwVGltZXI6OnNwVC52YWxpZGF0aW9uKHRlc3QkU2VwYWwuTGVuZ3RoLHByZWQpCnZhckltcFBsb3QocmYubW9kZWwpCmBgYAoKCgpb7LC46rOg7IKs7J207Yq4XShodHRwczovL2JjaG8udGlzdG9yeS5jb20vMTIwNikKW+ywuOqzoOyCrOydtO2KuDJdKGh0dHBzOi8vbS5ibG9nLm5hdmVyLmNvbS9Qb3N0Vmlldy5uaG4/YmxvZ0lkPWxhb25wbGUmbG9nTm89MjIwODY3NzY4MTkyJnByb3h5UmVmZXJlcj1odHRwcyUzQSUyRiUyRnd3dy5nb29nbGUuY29tJTJGKQo=