6.1 데이터 마이닝
6.1.1 데이터 마이니의 개념
데이터 마이닝은 대용량 데이터에서 쉽게 드러나지 않는 유용한 정보를 찾아내는 과정이다. 대용량 데이터에서 의미 있는 패턴을 파악하거나 예측하여 의사결정에 활용하는 방법이라 할 수 있다.
통계 분석은 가설에 따라 분석이나 검증을 하지만, 데이터 마이닝은 다양한 수리 알고리즘을 이용해 데이터에서 의미있는 정보를 찾아내는 방법을 통칭한다.
지도학습중 분류는 데이터를 여러 값 중 하나로 분류하는 범주 타겟으로, 회귀분석은 데이터 값을 예측, 추정하는 수치 타겟으로 나타낸다.
비지도학습은 데이터 내에서 관계와 유사성을 파악하는 것에 목적이 있다. 이는 예측이 목적인 지도학습과 차이를 보이는 점이다.
6.1.2 데이터 마이닝의 대표적 기능
분류와 계층 확률 추정
분류는 어느 계층에 속할지를 예측하는 것으로(예, 아니오) 이고 새롭게 나타난 현상을 검토하여 기존의 분류 정의된 집합에 배정하는 것을 의미한다.
데이터 마이닝 프로세스는 고객이 어느 계층에 속할지 결정하는 모델을 생성하는데 이 과정을 점수화(scoring) 또는 계층 확률 추정(class probability estimation) 이라고 부른다.
유사도 매칭
유사도 매칭(similarity matching)은 고객에게 제품을 추처할 때 사용하는 가장 많이 사용하는 방법중 하나로, 구매하였거나 선호하는 제품의 관점에서 현재 고객과 유사한 사람을 찾아내는 것이다.
군집화
군집화(clustering) 는 데이터의 기초 조사 (탐색적 데이터 분석 EDA)를 수행할 때 어떤 그룹이 자연스럽게 만들어 지는지를 알려주고, 군집이 존재하면 다른 데이터 마이닝 작업을 수행해볼 필요가 있다는 점을 알 수 있기 때문에 매우 유용한 방법이다. 군집화는 미리 정의된 기준이나 예시에 의존하지 않고 특정 목적이 없는 상태에서 유사도에 의해 그룹화 된다. 주로 데이터 마이닝이나 모델링 준비 단계에서 주로 사용된다.
회귀분석
어떤 일이 얼마나 많이 일어나는지를 예측하는 것이다. 예로 ‘이 고객이 이동전화 서비스를 얼마나 많이 사용할지’ 와 같은 질문이다. 여기서 예측할 변수는 서비스 사용량이며 다른 유사한 고객과 그들의 사용량 이력 데이터를 살펴보고 모델을 만들수 있다. 즉, 회귀분석은 어떤 고객에 대한 특정 변수의 값을 추정하는 모델을 만드는 것이다.
동시발생 그룹화 (co-occurrence grouping)
동시발생 그룹화 는 빈발항목 집합 마이닝 (frequent itemset mining), 연관성규칙 발견(Association rule discovery), 장바구니분석(market-basket analysis) 이라고도 한다. 이는 개체에 관련된 거래에 기반하여 개체 간의 연관성을 찾아낸다. 예로 ‘일반적으로 어떤 상품을 함께 구매하는가’ 와 같은 질문을 들수있다.
데이터 축소(data reduction)
데이터 축소는 많은 변수의 데이터 즉 고차원의 데이터에서 중요 정보를 상당수 담고 있는 더 적은 변수의 데이터(저차원 데이터) 셋으로 바꾸는 것이다. 적은 변수의 데이터셋은 처리하기 더 쉽고 정보를 찾아내기도 쉬워진다. 일반적으로 데이터 축소를 하면 정보는 손실되지만 데이터에 대한 통찰력을 얻을수 있는 장점이 있다.
6.1.3 데이터 마이닝 추진 단계
1 단계: 목적 정의
데이터 마이닝을 통해 얻고자 하는 것이 무엇인지 명확한 목적을 정의하는 단계이다. 1 단계부터 전문가가 참여하여 목적에 따라 사용할 데이터 마이닝 모델과 필요한 데이터를 정의한다.
2 단계: 데이터 준비
고객정보, 거래정보, 상품마스터, 웹로그데이터, 사회연결망 데이터 등 다양한 데이터를 활용할 수 있도록 수집하는 단계다. IT 부서와 사전협의를 통해 데이터 접근 부하에 문제가 없도록 조율. 데이터 정제를 통해 데이터의 품질을 보장하고 데이터 마이닝 기법 적용에 문제가 없도록 데이터 양을 충분히 확보한다.
3 단계: 데이터 가공 혹은 전처리
데이터 마이닝 기법 적용이 가능하도록 데이터를 가공하는 단계. 모델링 목적에 따라 목적 변수를 정의하고 필요한 데이터를 데이터 마이닝 소프트웨어에 적용할수 있도록 적합한 형식으로 가공한다.
4 단계: 데이터 마이닝 기법 적용
준비한 데이터에 데이터 마이닝 기법을 적용하는 단계로 데이터 마이닝 소프트웨어를 활용하여 목적하는 정보를 추출한다.
5 단계: 데이터 마이닝 적용 결과 검증 및 확산
데이터 마이닝으로 추출한 정보를 검증하는 단계로 테스트 마케팅이나 과거 데이터를 활용해 테스트를 수행할 수도 있다. 검증이 완료되면 IT 부서와 협의하여 상시적으로 데이터 마이닝 결과를 업무에 적용할수 있도록 자동화 방안을 협의한다. 보고서를 작성하여 경영진 및 구성원에게 연간 추가 수익과 투자대비성(ROI)등으로 기대 효과를 전파한다.
6.1.4 분류분석의 주요 모델
의사결정나무: 목표변수와 가장 연관성이 높은 변수의 순서대로 지니지수나 엔트로피 등이 낮아지는 방향으로 트리 형태로 가지를 분할하면서 분류 기법을 만들어내는 기법. 이것을 분할 정복기법 이라고도 한다
로지스틱 회귀: 설명변수값이 주어졌을 때, 목표변수값이 특정 부류에 속할 확률이 로지스틱 함수 형태를 따른다고 가정해 최대 우도 추정 방법(maximum likelihood estimation) 을로 목표변수의 확률을 추정하는 기법.
인공신경망: 인간 뇌의 뉴런작용 형태에서 모티브를 얻을 기법으로 입력, 은닉, 출력 노드로 구성하여 복잡한 분류나 수치 예측 문제를 해결할 수 있도록 하는 분석 기법.
랜덤 포레스트: 주어진 데이터로부터 여러 개의 다양한 의사결정 트리를 만들어 각 의사결정 트리의 예측결과를 투표형식으로 집계하여 최종 분류 결과를 결정하는 앙상블 형태의 기법.
서포트 벡터 머신: 특정 데이터를 분류하는데 있어 서로 다른 분류에 속한 데이터 간의 간격이 최대화가 되는 평면을 찾아 이를 기준으로 분류하는 기법.
나이브 베이즈: 베이즈 정리에 근거하여 목표변수가 발생할 조건부 확률을 사전 확률과 우도 함수의 곱으로 표현하여 어떤 분류 항목에 속할지를 계산하여 확률이 높은 쪽으로 분류하는 기법.
K-최근접 이웃: 특정 데이터 좌표점과 다른 나머지 데이터 좌표점 간의 거리에 기반을 두어 가장 가까운 k개 점들의 목표변수값을 다수결로 분류하는 기법. 게으른 학습이라고도 한다.
6.2 의사결정나무
의사결정 나무 모델은 의사결정 규칙을 나무 구조에 의한 추론 규칙으로 표현하여 전체 자료를 몇 개의 소집단으로 분류하거나 예측을 수행하는 분석 방법이다.
목표변수가 이산형인 경우 분류나무라고 하고, 연속형인 경우 회귀나무라고 한다.
목표변수가 이산형인 분류나무의 경우 상위 노드에서 가지 분할을 수행할 때 분류 변수와 분류 기준값의 선택 방법으로 카이제곱 통계량의 p-value, 지니지수 엔트로피지수 등이 사용된다.
의사결정나무 모델은 시장조사, 광고조사, 의학연구, 품질관리 등의 다양한 분야에서 활용되고 있다.
createDataPartition()함수는 분류를 기준으로 훈련용 데이터에서 사용할 데이터를 설정한 비율로 분리한다.
predict() 함수는 테스트 데이터로 의사결정나무 모델을 사용한 예측을 수행한다.
rpart.plot 패키지의 prp()함수로 적합된 의사결정나무 모델을 시각화한다.
6.2.1 의사경정나무 모델의 개념
의사결정나무 모델은 의사겨정 규칙을 나무구조에 의한 추론 규칙으로 표현하여 전체 자료를 몇 개의 소집단으로 분류하거나 예측을 수행하는 분석 방법이다.
6.2.2 분류 변수와 분류 기준값의 선택 방법
분류 변수와 분류 기준값의 선택 방법으로 카이제곱 통계량의 p-value, 지니지수, 엔트로피지수 등이 사용된다. 선택 기준에 의한 분할이 일어날 때 카이제곱 통계랴의 p-value는 그값이 작을수록 자식노드 내의 불확실성이 큼을 나타내며, 지니지수나 엔트로피지수 역시 그값이 클수록 자식노드 내의 이질성이 큼을 의미한다. 따라서 이 값들이 가장 작아지는 방향으로 가지 분할을 수행하게 된다.
데이터가 얼마나 잘 분리되었는지를 평가하기 위해서는 일반적으로 지니지수를 사용하며 이러한 지니지수는 노드에 여러 분류가 섞여 있을수록 높고, 노드에 하나의 분류만 존재할 때 가장 낮아진다. 즉, 노드 분리 후 각 노드의 불확실성 측정지수가 낮아질수록 트리분류가 잘 된 것으로 볼 수 있다.
6.2.3 의사결정나무의 구조
목표변수가 연속형인 회귀나무의 경우 분류 변수와 분류 기준값의 선택 방법으로 F-통계량은 그 값이 클수록 오차의 변동에 비해 처리의 변동이 크다는 것을 의미하며, 이는 자식마디 사이가 이질적이다는 것을 의미하므로 이 값이 커지는 (p-value 작아지는) 방향으로 가지 분할을 수행하게 된다. 분산의 감소량도 이 값이 최대화되는 방향으로 가지 분할을 수행하게 된다.
뿌리마디: 맨 위의 마디를 뿌리마디(root node)
부모마디: 상위마디가 하위마디로 분기될 때 상위마디
가지분할: 나무의 가지를 생성하는 과정이다
가지치기: 생성된 가지를 잘라내어 모델을 단순화하는 과정을 말한다.
깊이: 뿌리마디부터 최종마디까지의 중간마디들의 수
의사결정나무 장, 단점
장점:
구조가 단순해서 해석이 용이하다
분류, 수치예측 모두 활용가능
선형성, 정규성, 등분산성 등의 수학적 가정이 불필요하다.
데이터에 결측값이 있는 경우에도 효과적으로 처리가능
중요한 변수만 선별할 수있고, 이를 통해 다른 추가 분석을 위한 통찰력을 얻을 수 있다.
분류 결과에 대한 규칙기반의 해석이 가능하여 결과 해석에 유용하다.
단점:
연속형 입력변수를 비연속적인 값으로 취급하므로, 분리의 경계점 근방에서 예측 오류 가능성이 있다.
선형 또는 주효과 모델과 같은 해석이 불가능함으로 모델식을 수립해야 하는 경우 적용이 어렵다.
훈련용 데이터에 대한 약간의 변경 발생 시 트리 분류 결정 논리에 큰 변화를 가져온다
모델이 쉽게 과적합되거나 과소적합 될 수 있다.
트리가 너무 커질 경우 패턴을 이해하기가 쉽지 않다.
의사결정나무 모델을 위한 알고리즘에는 CHAID(chi-squared automatic interaction detection), CART(classification and regression tree), ID3, C4.5, C5.0 등이 있다.
과적합: 훈련용 데이터에 대해서는 높은 정확도를 나타내지만 새로운 데이터에 대해서는 예측을 잘하지 못하는 것을 말한다.
과소적합: 데이터를 충분히 반영하지 못해(예로 샘플 수가 충분하지 않은 경우) 잡음이 많이 섞여 있어 낮은 성과를 보이는 문제.
6.2.4 의사결정나무 분석 예제 (rpart 함수)
다음은 rpart 패키지의 rpart() 함수로 아이리스 데이터셋을 이용한 의사결정나무 분석을 수행하는 예제다.
1. 아이리스 데이터셋을 훈련용 데이터 와 테스트 데이터로 분리하기
## Loading required package: ggplot2
## Loading required package: lattice
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## 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
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
# 80프로 선
parts<-createDataPartition(iris$Species, p=0.8)
summary(parts)
## Length Class Mode
## Resample1 120 -none- numeric
# 트레이닝 데이터
data.train<-iris[parts$Resample1,]
table(data.train$Species)
##
## setosa versicolor virginica
## 40 40 40
# 테스트 데이터
data.test<-iris[-parts$Resample1,]
table(data.test$Species)
##
## setosa versicolor virginica
## 10 10 10
2. 훈련용 데이터로 의사결정나무 모델 학습하기
library(rpart)
# 의사결정나무 모델 학습을 위해 종석변수를 Species, 독립변수를 Sepal.Length, Sepal.Width, Petal.Length, Petal.Width
dt.m<-rpart(Species~. ,data=data.train)
print(dt.m)
## n= 120
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 120 80 setosa (0.33333333 0.33333333 0.33333333)
## 2) Petal.Length< 2.45 40 0 setosa (1.00000000 0.00000000 0.00000000) *
## 3) Petal.Length>=2.45 80 40 versicolor (0.00000000 0.50000000 0.50000000)
## 6) Petal.Width< 1.75 43 3 versicolor (0.00000000 0.93023256 0.06976744) *
## 7) Petal.Width>=1.75 37 0 virginica (0.00000000 0.00000000 1.00000000) *
3. 적합된 의사결정나무 모델 시각화
plot(dt.m, compress = TRUE, margin = 0.3)
text(dt.m,cex = 1.2)

테스트 데이터로 예측을 수행, 의사결정나무 모델의 성능 평가하기
dt.m.pred<-predict(dt.m, newdata = data.test, type = 'class')
confusionMatrix(data.test$Species, dt.m.pred)
## Confusion Matrix and Statistics
##
## Reference
## Prediction setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 9 1
## virginica 0 2 8
##
## Overall Statistics
##
## Accuracy : 0.9
## 95% CI : (0.7347, 0.9789)
## No Information Rate : 0.3667
## P-Value [Acc > NIR] : 1.888e-09
##
## Kappa : 0.85
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: setosa Class: versicolor Class: virginica
## Sensitivity 1.0000 0.8182 0.8889
## Specificity 1.0000 0.9474 0.9048
## Pos Pred Value 1.0000 0.9000 0.8000
## Neg Pred Value 1.0000 0.9000 0.9500
## Prevalence 0.3333 0.3667 0.3000
## Detection Rate 0.3333 0.3000 0.2667
## Detection Prevalence 0.3333 0.3333 0.3333
## Balanced Accuracy 1.0000 0.8828 0.8968
정분류율 (Accuracy)는 100 프로이다.
5. rpart.plot 로 모델 시각화
library(rpart.plot)
prp(dt.m, extra = 2)

Petal.Length 가 2.5 보다 적은개체는 40 개 있는데 그중 40개 전부가 setosa 이다.
Petal.Length 가 2.5 보다 크고 Petal.With 가 1.8 보다 작은 개체는 44 개인데 그중 39 개가 versicol 이다.
Petal.Length 가 2.5 보다 크고 Petal.With 가 1.8 보다 큰 개체는 36 개인데 그중 35 개는 virginic 이다.
6.2.5 의사결정나무 분석 예제 (cnee 함수)
1. 훈련용 데이터로 의사결정나무 모델 학습하기
## Loading required package: grid
## Loading required package: mvtnorm
## Loading required package: modeltools
## Loading required package: stats4
## Loading required package: strucchange
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: sandwich
dt.m2<- ctree(Species ~ ., data = data.train)
print(dt.m2)
##
## Conditional inference tree with 4 terminal nodes
##
## Response: Species
## Inputs: Sepal.Length, Sepal.Width, Petal.Length, Petal.Width
## Number of observations: 120
##
## 1) Petal.Length <= 1.9; criterion = 1, statistic = 112.878
## 2)* weights = 40
## 1) Petal.Length > 1.9
## 3) Petal.Width <= 1.7; criterion = 1, statistic = 55.55
## 4) Petal.Length <= 4.8; criterion = 0.999, statistic = 13.319
## 5)* weights = 36
## 4) Petal.Length > 4.8
## 6)* weights = 7
## 3) Petal.Width > 1.7
## 7)* weights = 37
2. 시각화

3. 테스트 데이터로 예측, 의사결정나무 모델 성능 평가
dt.m2.pred<-predict(dt.m2, newdata = data.test)
confusionMatrix(data.test$Species, dt.m2.pred)
## Confusion Matrix and Statistics
##
## Reference
## Prediction setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 9 1
## virginica 0 2 8
##
## Overall Statistics
##
## Accuracy : 0.9
## 95% CI : (0.7347, 0.9789)
## No Information Rate : 0.3667
## P-Value [Acc > NIR] : 1.888e-09
##
## Kappa : 0.85
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: setosa Class: versicolor Class: virginica
## Sensitivity 1.0000 0.8182 0.8889
## Specificity 1.0000 0.9474 0.9048
## Pos Pred Value 1.0000 0.9000 0.8000
## Neg Pred Value 1.0000 0.9000 0.9500
## Prevalence 0.3333 0.3667 0.3000
## Detection Rate 0.3333 0.3000 0.2667
## Detection Prevalence 0.3333 0.3333 0.3333
## Balanced Accuracy 1.0000 0.8828 0.8968
정분류율(Accuracy) 는 0.9667.
6.3 로지스틱 회귀
로지스틱 회귀 모델은 목표변수 (또는 종속변수) 가 범주형인 경우에 적용되는 회귀분석 모델이다.
오즈는 성공할 확률이 실패할 확률의 몇 배인지를 나타내는 확률이다.
complete.cases() 함수는 해당 행의 모든 값이
로지스틱 회귀 모델의 개념 NA 가 아닌 경우 TRUE, 해당 행의 값이 하나라도 NA 를 포함하고 있는 경우 FALSE값을 반환한다.
duplicated() 함수는 중복 값이 존재하는 경우 TRUE, 아닌 경우 FALSE를 출력한다.
performanceAnalytics 패키지의 chart.Correlation()함수는 산점와 상관계수를 출력한다.
GGally 패키지의 ggcorr()함수는 설명변수 간의 상관계수 히트맵을 시각화한다.
fmsb 패키지의 VIF()함수는 분산팽창지수를 출력한다.
scale()함수는 설명변수를 평균이 0, 분산이 1인 값으로 표준화한다.
glm()함수는 로지스틱 회귀 모델을 생성한다.
step() 함수의 direction = ‘backward’ 옵션은 후진제거법을 사용해 모델을 적합한다.
anova()함수는 하나 이상의 적합된 모델에 대한 분산분석을 수행한다. 분산분석은 모델을 평가하거나 모델 간의 비교를 위해 사용된다.
6.3.1 로지스틱 회귀 모델의 개념
로지스틱 회귀모델은 목표변수가 범주형인 경우에 적용되는 회귀분석 모델이다.
새로운 설명변수 또는 독립변수의 값이 주어질 때 목표변수의 각 범주 또는 집단에 속할 확률이 얼마인지를 추정/예측 하여 추정확률의 기준치에 따라 분류하는 목적으로 사용될 수 있다. 이때 모델의 적합을 통해 추정된 확률을 사후확률 이라고 한다. (posterior probability)
선형 회귀와 로지스틱 회귀 비교
선형회귀 종속변수: 연속형 변수; 로지스틱 종속변수: 이항 반응변수 (0,1)
선형회귀 계수추정법: 최소제곱법; 로지스틱 계수추정법: 최대우도추정법
선형회귀 모델 검정: F 검정, T 검정; 로지스틱 모델검정: 카이제곱 검정
로지스틱 회귀 모델의 장, 단점 비교
장점:
선형통계 모델의 이론에 기반한 정교하고 체계적인 모수 추정이 가능하다.
확률 모델이므로 목표변수의 범주 확률값을 추정할수 있다.
추정된 모델의 계수에 대한 해석이 가능하며, 독립변수들의 유의성 및 영향력 등 결과 분석 시 유용한 해석이 가능하다.
단점:
데이터셋의 차원이 매우 많을 때 모델의 추정 정확도가 다른 분류 기법에 비해 좋지 않다.
복잡한 비선형적 분류가 필요한 경우에는 분류 정확도가 좋지 않다.
추정 방법상 x 값이 매우 커지거나 작아지면 확률값이 1 혹은 0 에 매우 가까워져서 수치계산 정확도가 떨어지게 되며, 반복 계산 시 과적합이 빈번하게 발생한다.
6.3.2 로지스틱 회귀 모델 예제(glm 함수)
다음은 glm 함수를 이용해 mlbench 패키지의 유방암 데이터셋으로 로지스틱 회귀분석을 수행하는 예제다. 이는 데이터 셋 소개, 탐색적 데이터 분석, 변수 선택, 목표변수와 설명변수 간의 상관분석, 모델 평가 순으로 진행한다.
1. 유방암 데이터셋 불러오기
library(mlbench)
data('BreastCancer')
str(BreastCancer)
## 'data.frame': 699 obs. of 11 variables:
## $ Id : chr "1000025" "1002945" "1015425" "1016277" ...
## $ Cl.thickness : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 5 3 6 4 8 1 2 2 4 ...
## $ Cell.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 1 1 2 ...
## $ Cell.shape : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 2 1 1 ...
## $ Marg.adhesion : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 5 1 1 3 8 1 1 1 1 ...
## $ Epith.c.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 7 2 3 2 7 2 2 2 2 ...
## $ Bare.nuclei : Factor w/ 10 levels "1","2","3","4",..: 1 10 2 4 1 10 10 1 1 1 ...
## $ Bl.cromatin : Factor w/ 10 levels "1","2","3","4",..: 3 3 3 3 3 9 3 3 1 2 ...
## $ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 1 7 1 7 1 1 1 1 ...
## $ Mitoses : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ...
## $ Class : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
table(BreastCancer$Class)
##
## benign malignant
## 458 241
반응변수 Class 는 양성 begin 과 악성 malignant 두 가지 범주로 분류된다.
2. 결측값 확인 및 제거하기
colSums(is.na(BreastCancer))
## Id Cl.thickness Cell.size Cell.shape Marg.adhesion
## 0 0 0 0 0
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 0 16 0 0 0
## Class
## 0
## [1] 16
결측값이 16개가 Bare.nuclei 에 있다.complete.cases() 함수는 해당 행의 모든 값이 로지스틱 회귀 모델의 개념 NA 가 아닌 경우 TRUE, 해당 행의 값이 하나라도 NA 를 포함하고 있는 경우 FALSE값을 반환한다. 여기서 complete.cases를 이용하여 NA 가 들어있는 행을 드랍시킨다.
BreastCancer2<-BreastCancer[complete.cases(BreastCancer),]
# NA 여부를 확인ㄷ
sum(is.na(BreastCancer2))
## [1] 0
중복 데이터 확인 및 제거
## [1] 683
sum(duplicated(BreastCancer2))
## [1] 8
683항 중에서 8항이 중복되었음을 알수 있다. 중복 제거
BreastCancer3<-BreastCancer2[!duplicated(BreastCancer2),]
nrow(BreastCancer3)
## [1] 675
sum(duplicated(BreastCancer3))
## [1] 0
4. 반응변수 구성 분포 확인
table(BreastCancer3$Class)
##
## benign malignant
## 439 236
cat("total :", margin.table(table(BreastCancer3$Class)))
## total : 675
prop.table(table(BreastCancer3$Class))
##
## benign malignant
## 0.6503704 0.3496296
유방암 데이터 전처리 후 반응변수 Class 의 분할표를 보면 begin이 439개, malignant가 236개인 것을 볼수 있다.
5. 설명변수 간 다중공선성(Multicollinearity) 확인을 위해 반응변수 Class 를 Y, 설명변수를 X라는 데이터프레임으로 분리하고, 설명변수의 타입을 숫자타입으로 변환한다
# Y assigned 0 as begin, 1 as malignant
Y<-ifelse(BreastCancer3$Class =='malignant', 1, 0)
# choose the 2nd to 10th columns from BreastCancer3
X<-BreastCancer3[, c(2:10)]
# 새로 생긴 설명변수 X들의 타입을 숫자형으로 변형
X$Cl.thickness <-as.integer(X$Cl.thickness)
X$Cell.size <-as.integer(X$Cell.size)
X$Cell.shape <-as.integer(X$Cell.shape)
X$Marg.adhesion <-as.integer(X$Marg.adhesion)
X$Epith.c.size <-as.integer(X$Epith.c.size)
X$Bare.nuclei <-as.integer(X$Bare.nuclei)
X$Bl.cromatin <-as.integer(X$Bl.cromatin)
X$Normal.nucleoli <-as.integer(X$Normal.nucleoli)
X$Mitoses <-as.integer(X$Mitoses)
다음은 설명변수 간의 다중공선성이 존재하는지를 확인하기 위해 산점도 scatter plot, 상관계수 correlation coefficient, 분산팽창지수 variance inflation factor, 를 확인한다. 이것은 회귀모델에서 설명변수 간 독립성을 가정하기 때문이다.
6. 설명변수 간의 산점도와 상관계수 확인하기
library(PerformanceAnalytics)
## Loading required package: xts
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
chart.Correlation(X, histogram = TRUE, col = 'gray10', pch =1)

분석결과 매이 높은 상관관계 0.9 이상 을 보이는 설명변수 Cell.size 와 Cell.shape가 존재함으로 다중공선성을 의심할수 있다.
GGally 패키지의 ggcorr() 함수는 설명변수 간의 상관계수 히트맵을 시각화한다.
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
ggcorr(X, name = 'correlation', label = T)
분석결과 Cell.size 와 Cell.shape 두 속성의 상관계수가 0.9로 매우 높은 상관관계가 있다는 것을 알 수 있다.
8. fmsb 패키지의 VIF 함수로 설명변수 간의 분산팽창지수를 확인하기
## Registered S3 methods overwritten by 'fmsb':
## method from
## print.roc pROC
## plot.roc pROC
VIF(lm(Cl.thickness ~ ., data=X))
## [1] 1.90642
VIF(lm(Cell.size ~ ., data=X))
## [1] 7.109585
VIF(lm(Cell.shape ~ ., data=X))
## [1] 6.462976
VIF(lm(Marg.adhesion ~ ., data=X))
## [1] 2.523803
VIF(lm(Epith.c.size ~ ., data=X))
## [1] 2.530673
VIF(lm(Bare.nuclei ~ ., data=X))
## [1] 2.589232
VIF(lm(Bl.cromatin ~ ., data=X))
## [1] 2.903433
VIF(lm(Normal.nucleoli ~ ., data=X))
## [1] 2.461502
VIF(lm(Mitoses ~ ., data=X))
## [1] 1.403406
분석 결과 분산팽창지수 값이 10 이상인 설명변수는 없는 것을 볼 수 있다. fmsb 패키지의 VIF 함수는 분산팽창지수를 출력한다.
9. 설명변수 표준화하기
## Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size
## Cl.thickness 1.0000000 0.6408468 0.6526171 0.4894212 0.5191716
## Cell.size 0.6408468 1.0000000 0.9057554 0.7146499 0.7488287
## Cell.shape 0.6526171 0.9057554 1.0000000 0.6940289 0.7171865
## Marg.adhesion 0.4894212 0.7146499 0.6940289 1.0000000 0.6034792
## Epith.c.size 0.5191716 0.7488287 0.7171865 0.6034792 1.0000000
## Bare.nuclei 0.5939357 0.6898953 0.7108760 0.6764278 0.5827524
## Bl.cromatin 0.5564040 0.7594179 0.7378729 0.6717437 0.6226487
## Normal.nucleoli 0.5338912 0.7237118 0.7232412 0.6021876 0.6341289
## Mitoses 0.3548217 0.4667208 0.4485088 0.4245258 0.4846702
## Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## Cl.thickness 0.5939357 0.5564040 0.5338912 0.3548217
## Cell.size 0.6898953 0.7594179 0.7237118 0.4667208
## Cell.shape 0.7108760 0.7378729 0.7232412 0.4485088
## Marg.adhesion 0.6764278 0.6717437 0.6021876 0.4245258
## Epith.c.size 0.5827524 0.6226487 0.6341289 0.4846702
## Bare.nuclei 1.0000000 0.6791367 0.5879502 0.3495506
## Bl.cromatin 0.6791367 1.0000000 0.6688204 0.3532766
## Normal.nucleoli 0.5879502 0.6688204 1.0000000 0.4363314
## Mitoses 0.3495506 0.3532766 0.4363314 1.0000000
실행 결과 분산이 1인것을 볼 수 있다. scale()함수는 설명변수를 평균이 0, 분산이 1인값으로 표준화한다. 테이터를 표준화하면 변수의 측정 단위 또는 범위간의 편차를 없애준다.
10. 데이터셋을 훈련용 데이터 80 % 와 테스트 데이터 20 % 로 분리하기
BreastCancer4<- data.frame(Y, X2)
set.seed(123)
train<-sample(1:nrow(BreastCancer4), size = 0.8 * nrow(BreastCancer4), replace = F)
test<-(-train)
Y.test<-Y[test]
scales::percent(length(train)/nrow(BreastCancer4))
## [1] "80%"
훈련데이터와 테스트 데이터는 80%, 20% 로 분리된것을 알 수 있다.
12. 훈련용 데이터로 로지스틱 회귀 모델 적합하기
glm.fit<-glm(Y~., data = BreastCancer4[train,], family = binomial)
summary(glm.fit)
##
## Call:
## glm(formula = Y ~ ., family = binomial, data = BreastCancer4[train,
## ])
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -3.4697 -0.1092 -0.0645 0.0272 2.0358
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.25451 0.35899 -3.495 0.000475 ***
## Cl.thickness 1.42695 0.43656 3.269 0.001081 **
## Cell.size -0.03872 0.64364 -0.060 0.952034
## Cell.shape 0.94102 0.68520 1.373 0.169641
## Marg.adhesion 0.61825 0.39002 1.585 0.112929
## Epith.c.size -0.05972 0.44169 -0.135 0.892441
## Bare.nuclei 1.53896 0.38169 4.032 5.53e-05 ***
## Bl.cromatin 1.28305 0.47518 2.700 0.006931 **
## Normal.nucleoli 0.73540 0.38577 1.906 0.056612 .
## Mitoses 0.97173 0.59059 1.645 0.099896 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 697.996 on 539 degrees of freedom
## Residual deviance: 83.888 on 530 degrees of freedom
## AIC: 103.89
##
## Number of Fisher Scoring iterations: 8
영향력이 없는 변수를 제거하기위해 후진제거법을 사용하여 모델을 적합하기
step(glm.fit, direction = 'backward')
## Start: AIC=103.89
## Y ~ Cl.thickness + Cell.size + Cell.shape + Marg.adhesion + Epith.c.size +
## Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses
##
## Df Deviance AIC
## - Cell.size 1 83.891 101.89
## - Epith.c.size 1 83.906 101.91
## - Cell.shape 1 85.656 103.66
## <none> 83.888 103.89
## - Marg.adhesion 1 86.366 104.37
## - Normal.nucleoli 1 87.749 105.75
## - Mitoses 1 88.045 106.05
## - Bl.cromatin 1 92.061 110.06
## - Cl.thickness 1 97.034 115.03
## - Bare.nuclei 1 101.994 119.99
##
## Step: AIC=101.89
## Y ~ Cl.thickness + Cell.shape + Marg.adhesion + Epith.c.size +
## Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses
##
## Df Deviance AIC
## - Epith.c.size 1 83.912 99.912
## <none> 83.891 101.891
## - Marg.adhesion 1 86.410 102.410
## - Cell.shape 1 87.081 103.081
## - Normal.nucleoli 1 87.923 103.923
## - Mitoses 1 88.162 104.162
## - Bl.cromatin 1 92.600 108.600
## - Cl.thickness 1 97.347 113.347
## - Bare.nuclei 1 101.994 117.994
##
## Step: AIC=99.91
## Y ~ Cl.thickness + Cell.shape + Marg.adhesion + Bare.nuclei +
## Bl.cromatin + Normal.nucleoli + Mitoses
##
## Df Deviance AIC
## <none> 83.912 99.912
## - Marg.adhesion 1 86.415 100.415
## - Cell.shape 1 87.156 101.156
## - Normal.nucleoli 1 87.949 101.949
## - Mitoses 1 88.162 102.162
## - Bl.cromatin 1 93.061 107.061
## - Cl.thickness 1 97.348 111.348
## - Bare.nuclei 1 103.415 117.415
##
## Call: glm(formula = Y ~ Cl.thickness + Cell.shape + Marg.adhesion +
## Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses, family = binomial,
## data = BreastCancer4[train, ])
##
## Coefficients:
## (Intercept) Cl.thickness Cell.shape Marg.adhesion
## -1.2573 1.4211 0.8973 0.6136
## Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 1.5225 1.2598 0.7230 0.9679
##
## Degrees of Freedom: 539 Total (i.e. Null); 532 Residual
## Null Deviance: 698
## Residual Deviance: 83.91 AIC: 99.91
후진제거법을 통하여 AIC 가 99.91로 가장작은 glm(formula = Y ~ Cl.thickness + Cell.shape + Marg.adhesion + Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses, family = binomial, data = BreastCancer4[train, ]) 이 가장 우수한 모델이라고 판단할 수 있다.
14. 후진제거법에서 채택된 모델을 적합하고 모델의 유의 검정하기
glm.fit.2<-glm( Y ~ Cl.thickness + Cell.shape + Marg.adhesion +
Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses, family = binomial,
data = BreastCancer4[train, ])
summary(glm.fit.2)
##
## Call:
## glm(formula = Y ~ Cl.thickness + Cell.shape + Marg.adhesion +
## Bare.nuclei + Bl.cromatin + Normal.nucleoli + Mitoses, family = binomial,
## data = BreastCancer4[train, ])
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -3.4510 -0.1087 -0.0651 0.0274 2.0557
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.2573 0.3589 -3.504 0.000459 ***
## Cl.thickness 1.4211 0.4332 3.281 0.001036 **
## Cell.shape 0.8973 0.5237 1.713 0.086669 .
## Marg.adhesion 0.6136 0.3868 1.586 0.112694
## Bare.nuclei 1.5225 0.3629 4.195 2.72e-05 ***
## Bl.cromatin 1.2598 0.4511 2.793 0.005229 **
## Normal.nucleoli 0.7230 0.3733 1.937 0.052777 .
## Mitoses 0.9679 0.5870 1.649 0.099160 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 697.996 on 539 degrees of freedom
## Residual deviance: 83.912 on 532 degrees of freedom
## AIC: 99.912
##
## Number of Fisher Scoring iterations: 8
anova(glm.fit.2,test = 'Chisq')
## Analysis of Deviance Table
##
## Model: binomial, link: logit
##
## Response: Y
##
## Terms added sequentially (first to last)
##
##
## Df Deviance Resid. Df Resid. Dev Pr(>Chi)
## NULL 539 698.00
## Cl.thickness 1 323.28 538 374.71 < 2.2e-16 ***
## Cell.shape 1 208.78 537 165.94 < 2.2e-16 ***
## Marg.adhesion 1 30.76 536 135.18 2.920e-08 ***
## Bare.nuclei 1 28.39 535 106.79 9.932e-08 ***
## Bl.cromatin 1 14.15 534 92.64 0.0001692 ***
## Normal.nucleoli 1 4.48 533 88.16 0.0342509 *
## Mitoses 1 4.25 532 83.91 0.0392408 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
위의 최종 모델로부터 다른 설명변수가 통제되었을 경우 설명변수 Xi 가 한 단위 증가할때 유방암 악성일 확률의 오즈는 exp(beta(i)) 만큼 증가한다. 예로 Mitoses의 회귀계수가 0.96 이므로 Mitoses 속성이 한단위 증가할 때 유방암이 악성의 확률의 오즈는 exp(0.96) = 2.62 배 증가한다. 모든 설명변수의 p-value(prob Chi)가 0.05 보다 작아 모델이 유의미하다는 결론을 도출할 수 있다. anova 함수는 하나 이상의 적합된 모델에 대한 분산분석을 수해한다. 이는 모델을 평가하거나 모델간의 비교를 위해 사용된다.
테스트 데이터로 모델 성능 평가 수행하기
glm.probs<-predict(glm.fit.2, BreastCancer4[test, ], type = 'response')
# probability 형태의 예측
head(glm.probs)
## 1 3 9 18 22 33
## 0.015966687 0.008922727 0.013332039 0.009709133 0.998791643 0.998064958
# threshold 를 0.5로 설정
glm.pred<-ifelse(glm.probs > 0.5, 1, 0)
table(Y.test, glm.pred)
## glm.pred
## Y.test 0 1
## 0 87 0
## 1 4 44
# Accuracy. logic 변수는 mean으로 추출
mean(Y.test == glm.pred)
## [1] 0.9703704
# Error rate
mean(Y.test != glm.pred)
## [1] 0.02962963
16. ROC 그래프와 AUC 확인하기
library(ROCR)
pr <- prediction(glm.probs, Y.test)
prf <- performance(pr, measure = 'tpr', x.measure = 'fpr')
plot(prf, main = 'ROC Curve')

auc <- performance(pr, measure = 'auc')
auc<-auc@y.values[[1]]
auc
## [1] 0.9978448
분석결과 ROC 곡선이 매이 이상적이고 AUC가 약 0.99 로 매우 우수한 모델이라 할 수 있다.
참고: VIF 값이 5 이상이면 다중공선성이 있다고 보고, VIF 값이 10 이상이면 다중공선성이 매우 심각하다고 평가한다.
6.4 인공신경망
인공신경망 모델: 생물체의 뇌신경계를 모방하여 입력신호와 출력신호 간의 관계를 모델화하는 기법이다.
단층신경망: 입력층이 은닉층을 거치지 않고 직접 출력층에 연결 (오로지 입력층과 출력층으로만 구성 되어 있다.)
다층신경망: 단층신결망 모델과 달리 입력층과 출력층 사이에 여러 개의 은닉층을 가질 수 있다.
피드포워드신경망: 정보가 전방으로 전달되는 것은 생물학적 신경계에서도 유사하게 나타나며, 이러한 원리를 인공신경망에 적용한 것을 말한다.
dupicated 함수는 중복 값이 존재하는 경우 TRUE 아닌 경우 FALSE로 값을 출력한다.
GGally 패키지의 ggcorr 함수는 설명변수 간의 상관계수 히트맵을 시각화한다.
fmsb 패키지의 VIF 함수는 분산팽창지수를 추력한다
scale 함수는 설명변수를 평균이 0, 분산이 1 인값으로 표준화한다.
nnet 함수는 활성함수로 시그모이드 또는 선형 출력을 사용한다.
neuralnet 함수는 다양한 역전파 알고리즘을 통해 모델을 생성한다.
compute 함수는 각 뉴런의 출력값을 계산한다.
6.4.1 인공신경망 모델의 개념
인공신경망 ANN 모델은 생물체의 뇌신경계를 모방하여 입력신호와 출력 신호간의 관계를 모델화하는 기법이다.
자연 뉴런이 시냅스를 통해 신호를 전달받는 과정에서 신호의 강도가 기준치보다 크면 뉴런은 활성화되고 신경돌기를 통하여 신호를 방출한다.
여기서 입력은 시냅스에 해당하고, 개별 신호의 강도에 따라 가중되며, 활성함수는 출력을 계산한다.
6.4.2 단층신경망/단층퍼셉트론
단층신경망은 입력층이 은닉층을 거치지 않고 직접 출력층에 연결되어 있다. 입력층과 출력층으로만 구성 되어있다.
인공신경망은 많은 데이터에 대해 학습을 거쳐 원하는 결과가 나오도록 (오차가 작아지는 방향으로 ) 가중치가 조정된다. 즉, 인공신경망은 가중치를 반복적으로 조정하며 학습한다.
6.4.3 다층신경망
다층신경망 또는 다층퍼셉트론 은 단층신경망과 달리 입력층과 출력층 사이에 여러 개의 은닉층을 가질 수 있다. 은닉층의 수는 의사결정 경계를 정하는데 중요하다. 은닉층의 수를 정할때 고려사항은
다층신경망은 단층신경망에 비해 훈련이 어렵다. 시그모이드 함수를 가지는 2개 층의 네트워크(1개 은닉층)는 임의의 의사결정 경계를 모델화할 수 있다.
각 층의 노드 수 (units) 는 다음을 결정하여 결정한다.
출력층 노드 - output units 의 수는 출력 범주의 수로 결정한다. 입력 - inputs 의 수는 입력 차원의 수로 결정한다. 은닉층 노드 - hidden units 의 수는 다음을 고려하여 정한다.너무 적으면 네트워크가 복잡한 의사결정 경계를 만들수 없다, 너무 많으면 네트워크의 일반화가 어렵다.
6.4.4 피드드포워드신경망 FNN
Feedforward Neural Network(FNN): 정보가 전방으로 전달되는 인공신경망, 신경세포를 오직 앞 방향으로만 연결시킨다. 어떤 신경세포 층도 이전의 신경세포 층과는 달리 연결되지 않기 때문에 피드포워드라는 이름을 갖게 되었다.
피드포워드신경망에서 입력층은 입력 데이터를 받아들이는 기능이고, 입력층의 뉴런 또는 노드 개수는 입력 데이터의 특성 개수와 일치하다.
은닉층에서 뉴런 수가 너무 많으면 과적합이 발생하고, 뉴런 수가 너무 적으면 입력 데이터를 충분히 표현못하는 경우가 발생할 수 있다. 은닉층의 활성화 함수는 뉴런으로 모아진 신호를 좀 더 변별력 있는 상태로 전환하는 것이다.
학습(learning): 피드포워드신경망에서 원하는 결과를 얻기 위해서는 뉴런 사이에 정보 전달 과정에 작용하는 적당한 가중치를 알아내야 하는데 이것을 학습이라고 한다.
역전파(Back Propagation) 는 대표적인 지도학습 알고리즘으로 레이블된 학습 데이터를 가지고 여러 개의 은닉층을 가지는 피드포워드신경망을 학습시킬 때 사용된다. 이는 현재 신경망에서 가장 많이 사용되는 학습 알고리즘이다.
역전파 학습은 크게 3 단계 과정이 반복된다.
1. 피드포워드 과정
먼저 모든 층에 있는 가중치를 임의의 수로 초기화하고, 레이블된 학습 데이터를 입력층에서 입력받아 은닉층을 통해 출력층까지 피드포워드 한다.
2. 역전파 계산
피드포워드된 예상값과 실제값의 차이인 에러를 구하고, 에러를 최소화하는 가중치를 찾는 과정이다.
3. 가중치 조정
가중치 조정: 앞 단계에서 계산된 에러로 학습률(최소평균제곱의 미분값을 이용해 학습률을 선정) 만큼 수정된 가중치를 구하고, 델타 룰을 이영해 가중치를 조정한다.
인공신경ㅇ망 모델의 장, 단점
장점:
변수의 수가 많거나 입, 출력 변수 간의 복자반 비선형 문제에도 탁월한 성능을 보인다.
분류 및 수치 예측 문제에 모두 적용 가능하다.
통계적 기본 가정이 적고 유연한 모델을 만든다.
데이터 사이즈가 작거나 불완전 데이터, 노이즈 데이터가 있는 경우에도 다른 모델에 비하여 예측성능이 우수한 경우가 많다.
단점:
모델 결과 해석이 어려워서 은닉층의 노드들이 무엇을 표현하는지, 결과값 설명이 필요한 모델링에는 적합하지 않다.
은닉층의 수와 은닉노드 수의 결정이 어렵다.
나이브 베이즈 로지스틱 회귀 모델처럼 보다 단순한 분류 모델에 비해 컴퓨팅 연산에 많은 자원이 필요하다.
과적합 또는 과서적합이 발생하기 쉽다.
초기값에 따라 전체적 관점에서의 최적해가 아닌 지역 최적해가 선택될 수 있다.
많이 사용되는 활성함수
시그모이드 함수: 결과는 연속형이고 0<=y<=1 이며 ,로지스틱 회귀 모델과 유사하다. y = 1 / [1+exp(-z)]
계단함수: 결과는 이진형인 0 또는 1. y = 0 when z<0; y = 1 when z>=1
tanh 함수: 결과는 연속형이며 -1<=y<=1 이다. y = [exp(z)-exp(-z)]/[exp(z)+exp(-z)]
relu 함수: 입력값이 0 이하는 0, 0 초과는 x 값을 가지며 최근 딥러닝에서 많이 사용되는 함수이다. Yrelu = 0, if x <= 0 or Yrelu = x, if x > 0
softmax 함수: 표준화주수 또는 일반화 로지스틱 함수로도 불리며 출력값이 여러 개로 주어지고, 목표치가 다범주인 경우에 각 범주에 속할 사후 확률을 제공하는 함수이다.
가우스 함수: 결과는 연속형이며 0 <= y <= 1 이다. y = exp(-z^2 / 2)
부호 또는 threshold 함수: 결과는 이진형 -1 또는 1 이다. y = -1 , z < 0 or y = 1, z >= 1
참고: 지역해 - local minimum
신경망은 가중치를 임의의 값으로 초기화한 후에 가중치를 조절하면서 에러의 제곱학 (sum of squred error) 또는 엔트로피를 기준으로 최적화한다. 이는 수식으로 단번에 최적의 가중치를 찾는 것이 어렵기 때문에 반복적으로 답을 찾아가는 것이다. 지역해 문제는 에러를 최소화시키는 최적의 파라미터를 찾는 문제에 있어서 파라미터 공간에 수많은 지역적인 홀들이 존재하여 이러한 지역해에 빠질경우 전역해 (global minimum) 을 찾기 힘들게 되는 문제를 일컫는다.
6.4.5 인공신경망 분석 예제 with nnet()함수
다음은 nnet함수로 datasets 패키지의 자연 유산과 인공 유산 후의 불임에 대한 사례-대조 연구 데이터셋을 이용해 신경망 모델을 적합하는 예제다.
1. infer 데이터셋 불로오기
data('infert', package = 'datasets')
str(infert)
## 'data.frame': 248 obs. of 8 variables:
## $ education : Factor w/ 3 levels "0-5yrs","6-11yrs",..: 1 1 1 1 2 2 2 2 2 2 ...
## $ age : num 26 42 39 34 35 36 23 32 21 28 ...
## $ parity : num 6 1 6 4 3 4 1 2 1 2 ...
## $ induced : num 1 1 2 2 1 2 0 0 0 0 ...
## $ case : num 1 1 1 1 1 1 1 1 1 1 ...
## $ spontaneous : num 2 0 0 0 1 1 0 0 1 0 ...
## $ stratum : int 1 2 3 4 5 6 7 8 9 10 ...
## $ pooled.stratum: num 3 1 4 2 32 36 6 22 5 19 ...
table(infert$case) # 타깃 변수
##
## 0 1
## 165 83
infert 데이터는 8개의 변수와 248개의 관측값을 가지고 있다. 반응변수 case는 1(사례), 0 (대조)를 나타낸다. 반응변수의 분할표를 보면 0이 165개, 1이 83개인 것을 볼 수 있다.
2. 결측값, 중복데이터 확인 및 제거하기
# 토탈 NA
colSums(is.na(infert))
## education age parity induced case
## 0 0 0 0 0
## spontaneous stratum pooled.stratum
## 0 0 0
## [1] 248
# 토탈 중복데이터
sum(duplicated(infert))
## [1] 31
분석결과 결측값은 존재하지 않았고, 중복 데이터는 31개 있어 모두 제거한다.
# 중복된 데이터를 제거하고 infert2 라고 지정
infert2<-infert[!duplicated(infert), ]
nrow(infert2)
## [1] 217
## [1] 0
3. 반응변수 구성 분포 확인하기
table(infert2$case); cat('total:', margin.table(table(infert2$case)))
##
## 0 1
## 134 83
## total: 217
# 0 = 대조 , 1 = 사례
prop.table(table(infert2$case))
##
## 0 1
## 0.6175115 0.3824885
infert 데이터 전처리 후 반응변수의 분할표를 보면 0(대조) 이 134개, 1(사례)이 83개인 것을 볼 수있다.
4. 설명변수 간 다중공선성 확인을 위해 목표변수를 Y, 설명변수를 X라는 데이터 프레임으로 분리하기
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
##
## first, last
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
Y <- infert2$case
# 설명변수 중에서 education속성을 제외한 나머지 6개변수를 선택하여 X에 할당한다.
X <- infert2 %>%
select('age','parity','induced','spontaneous','stratum','pooled.stratum')
5. 설명변수 간의 산점도scatter plot와 상관계수값correlation coefficient 확인하기
library(PerformanceAnalytics)
chart.Correlation(X, histogram = TRUE, col = 'grey10', pch = 1)

분석결과 높은 상관계수(0.7이상) 을 보이는 설명변수는 stratum, pooled.stratum 가존재함으로 다중공선성을 의심할수 있다.
library(GGally)
ggcorr(X, name = 'correlation coefficient', label = T)

GGally 패키지의 ggcorr함수로 분석한 결과 stratum, pooled.stratum 두 속성의 상관계수가 0.7로 높은 상관관계가 있다는 것을 알 수 있다.
7. 설명변수 간의 분산팽창지수 - VIF 확인하기
library(fmsb)
VIF(lm(age ~ ., data = X))
## [1] 1.082952
VIF(lm(parity ~ ., data = X))
## [1] 2.278814
VIF(lm(induced ~ ., data = X))
## [1] 1.773881
VIF(lm(spontaneous ~ ., data = X))
## [1] 1.669696
VIF(lm(stratum ~ ., data = X))
## [1] 3.873179
VIF(lm(pooled.stratum ~ ., data = X))
## [1] 3.667886
분석 결과 분산팽창지수값이 5이상인 설명변수는 없는 것을 볼 수있다. VIF는 독립변수가 다중 공산성(Multicollnearity)의 문제를 갖고 있는지 판단하는 기준이며,주로 10보다 크면 그 독립변수는 다중공산성이 있다고 말합니다.
8. 설명변수 표준화하기
## age parity induced spontaneous stratum
## age 1.00000000 0.1067976 -0.07064768 -0.04451437 -0.19519692
## parity 0.10679763 1.0000000 0.40037688 0.34393582 -0.30761505
## induced -0.07064768 0.4003769 1.00000000 -0.30214629 -0.11164181
## spontaneous -0.04451437 0.3439358 -0.30214629 1.00000000 0.03446012
## stratum -0.19519692 -0.3076150 -0.11164181 0.03446012 1.00000000
## pooled.stratum -0.13929427 0.1420194 0.14741830 0.19379142 0.74825495
## pooled.stratum
## age -0.1392943
## parity 0.1420194
## induced 0.1474183
## spontaneous 0.1937914
## stratum 0.7482550
## pooled.stratum 1.0000000
실행 결과 분산이 1인것을 볼 수 있다. scale 함수는 설명변수를 평균이 0, 분산이 1인 값으로 표준화한다.
10. 데이터셋 을 훈련용 데이터와 테스트 데이터로 분리
infert.data<-data.frame(Y,X2)
set.seed(123)
train<-sample(1:nrow(infert.data), size = 0.8*nrow(infert.data), replace = F)
test<-(-train)
Y.test<-Y[test]
scales::percent(length(train)/nrow(infert.data))
## [1] "80%"
## [1] 159 207 179 14 195 170
실행 결과 데이터셋에서 80프로가 훈련용 데이터로 분리된것을 알 수 있다. 여기서 훈련용 데이터(train), 테스트 데이터(test), 테스트 반응변수 데이터 (Y.test) 에는 관측값이 들어있는 것이 아니라 인덱스 정보가 들어 있다.
11. 훈련용 데이터로 인공신경망 모델을 적합하기
library(nnet)
nn.fit<-nnet(Y ~ spontaneous + pooled.stratum + age + stratum, data = infert.data[train, ],
size = 2, rang = 0.1, decay = 5e-4, maxit = 200)
## # weights: 13
## initial value 43.233005
## iter 10 value 35.964349
## iter 20 value 33.572048
## iter 30 value 33.501981
## iter 40 value 33.494601
## iter 50 value 33.491939
## iter 60 value 33.490829
## iter 70 value 33.490809
## final value 33.490794
## converged
## a 4-2-1 network with 13 weights
## options were - decay=5e-04
## b->h1 i1->h1 i2->h1 i3->h1 i4->h1
## -4.26 -5.72 -2.76 -7.28 -1.90
## b->h2 i1->h2 i2->h2 i3->h2 i4->h2
## 4.79 -9.11 8.35 -2.16 -11.92
## b->o h1->o h2->o
## 0.62 1.21 -2.47
nnet 패키지에서 신경망의 파라미터는 엔트로피 또는 에러의 제곱합을 고려해 최적화 된다. 여기서 기본값은 에러의 제곱합 이다. 출력결과는 softmax를 사용해 확률과 같은 형태로 변환할 수 있고 과적합을 막기위해 가중치 감소 (weight decay)옵션을 제공한다. nnet() 함수는 활성함수로 시그모이드 또는 선형 출력(기본값은 시그모이드 함수, lineout = FALSE)을 사용한다.
12. 모델 적합 결과 시각화하기
## Loading required package: usethis
# import the plot function for nnet
source_url('https://gist.githubusercontent.com/fawda123/7471137/raw/466c1474d0a505ff044412703516c34f1a4684a5/nnet_plot_update.r')
## ℹ SHA-1 hash of file is "74c80bd5ddbc17ab3ae5ece9c0ed9beb612e87ef"
## Loading required package: scales
## Loading required package: reshape
##
## Attaching package: 'reshape'
## The following object is masked from 'package:dplyr':
##
## rename

위 그림에서의 선의 굵기는 연결선의 가중치에 비례한다.
13. 테스트 데이터로 모델 성능 평가 수행하기
nn.probs<-predict(nn.fit, infert.data[test, ])
nn.pred<-ifelse(nn.probs > 0.5, 1, 0)
table(Y.test, nn.pred)
## nn.pred
## Y.test 0 1
## 0 22 4
## 1 7 11
# Accuracy
mean(Y.test == nn.pred)
## [1] 0.75
14. ROC 그래프와 AUC 확인하기
library(ROCR)
pr<-prediction(nn.pred, Y.test)
prf<-performance(pr, measure = 'tpr', x.measure = 'fpr')
plot(prf, main = 'ROC Curve')

auc<-performance(pr, measure = 'auc')
auc<-auc@y.values[[1]]
auc
## [1] 0.7286325
분석결과 ROC 곡선이 완만하게 증가하고, AUC 가 약 0.6 이므로 적합된 모델이 매우 우수한 모델이라고는 할 수 없지만, 좋은 비즈니스 결과를 가져올 수도 있다.
6.4.6 인공신경망 분석 예제 with neuralnet 함수
다음은 neuralnet 패키지의 neuralnet함수를 이용해 infert 자료로 인공신경망 분석을 수행하는 예제다.
1. 훈련용 데이터셋을 이용해 인공신경망 모델을 적합하기
##
## Attaching package: 'neuralnet'
## The following object is masked from 'package:dplyr':
##
## compute
## The following object is masked from 'package:ROCR':
##
## prediction
net.fit<-neuralnet(formula = Y ~ spontaneous + pooled.stratum + age + stratum, data = infert.data[train, ], hidden = c(2,2), err.fct = 'ce', threshold = 0.01, linear.output = FALSE, likelihood = TRUE,stepmax = 1e7) # stepmax를 지정하였음
은닉층이 2개인 모델을 적용하기 위해 각각 은닉 노드의 수는 2개,2개로 한다. 이를 위해 neuralnet() 함수의 hidden = c(2,2) 옵션을 사용한다. threshold 옵션은 오차함수의 편미분에 대한 값으로 정지규칙(stop rule) 으로 사용된다. neauralnet() 함수는 다양한 역전파(back-propagation) 알고리즘을 통해 모델을 생성한다.
적합된 모델 시각화하기
실행 결과 입력층이 4개, 은닉층이 2개, 출력층이 1개인 모델로 적합된것을 볼수 있다.
3. 적합된 모델의 추가적인 정보 확인하기
## [1] "call" "response" "covariate"
## [4] "model.list" "err.fct" "act.fct"
## [7] "linear.output" "data" "exclude"
## [10] "net.result" "weights" "generalized.weights"
## [13] "startweights" "result.matrix"
$data : 전체자료
$covariate, 와 response : 모델 적합에 사용된 자료
$net.result: 적합값
$startweights: 가중치의 초기값
$weights: 가중치의 적합값
$result.matrix: 결과 행렬에 대한 정보
$generalized.weights: 일반화 가중치
4. 모델 적합에 사용된 자료와 적합된 값 확인하기
out<-cbind(net.fit$covariate, net.fit$net.result[[1]])
dimnames(out)<-list(NULL, c('spotaneous','pooled.stratum','age','stratum','nn-output'))
head(out)
## spotaneous pooled.stratum age stratum nn-output
## [1,] -0.8524475 1.0830316 -0.0602827 1.4351792 0.1758648
## [2,] -0.8524475 0.3814858 -1.2145191 1.1437304 0.1627067
## [3,] -0.8524475 -0.9046817 -0.6374009 -0.8131403 0.3441342
## [4,] -0.8524475 -0.3200601 -0.4450282 -1.1878602 0.3446074
## [5,] 0.4783518 0.4399479 -0.6374009 0.4775616 0.4359711
## [6,] -0.8524475 -1.7231518 -1.9840100 -1.3960380 0.3446367
5. 일반화 가중치(generalized weights)에 대한 시각화하기
par(mfrow = c(2,2))
gwplot(net.fit, selected.covariate = 'spontaneous', min = -2.5, max = 5)
gwplot(net.fit, selected.covariate = 'pooled.stratum', min = -2.5, max = 5)
gwplot(net.fit, selected.covariate = 'age', min = -2.5, max = 5)
gwplot(net.fit, selected.covariate = 'stratum', min = -2.5, max = 5)

분석 결과 일반화 가중치의 분포로부터 pooled.stratum, age, stratum은 대부분 값이 0 근처의 값을 가지므로 사례-대조 상태에 따른 효과가 미미하고 spontaneous는 일반화 가중치의 분산이 전반적으로 1보다 크기 때문에 비선형 효과를 가진다고 할 수 있다.
일반화 가중치는 다른 모든 공변량에 의존하므로 각 자료점에서 국소적인 기요도를 나타낸다. 예를 들어 , 동일변수가 몇몇 관측값에 대해서는 양의 영향을 가지며 다른 관측값에 대해서는 음의 영향을 가진다면 평균적으로 0에 가까운 영향을 갖는 것이 가능하다.
모든 자료에 대한 일반화 가중치의 분포는 특정 공변량의 효과가 선형인지의 여부를 나타낸다. 즉, 작은 분산은 선형 효과를 제시하며 큰 분산은 관측값 공간상에서 변화가 심하다는 것을 나타내므로 비선형적인 효과가 있음을 나타낸다
6. 테스트 데이터로 적합된 모델의 뉴런 출려값 계산하기 (예측값 구하기)
test.data.out<-compute(net.fit, infert.data[test, ])
head(test.data.out$net.result)
## [,1]
## 3 0.3419691
## 10 0.3446257
## 12 0.3442493
## 15 0.3445452
## 18 0.3446219
## 19 0.3446313
# confusion matrix
net.pred<-ifelse(test.data.out$net.result>0.5, 1, 0)
table(infert.data[test, ]$Y, net.pred)
## net.pred
## 0 1
## 0 22 4
## 1 12 6
# Accuracy
mean(net.pred == infert.data[test, ]$Y)
## [1] 0.6363636
6.5 알상블
앙상블 모델: 주어진 데이터로부터 여러 개의 모델을 학습한 후 결과를 종합하여 알고리즘의 안정성과 정확성을 높이는 방법이다.
배깅은 일반적인 모델을 만드는데 집중되어 있고 부스팅은 어려운 문제를 맞추는데 초점이 맞추어져 있다.
배깅은 각 샘플에서 나타난 결과를 일종의 중앙값으로 맞추기 때문에 과적합을 피할수 있다.
데이터 분할시 createDataPartition 함수를 사용하면 반응변수값의 비율이 원본 데이터와 같게 유지된다.
adabag 패키지의 bagging 함수는 배깅 모델을 생성하고 boosting 함수는 부스팅 모델을 생성한다.
부스팅은 순차적으로 학습시키고 학습하는 과정에서 오답에 대해 높은 가중치를 부여하지만 정답에 대해서는 낮은 가중치를 부여하기 때문에 오답에 더욱 집중할 수 있다.
랜덤 포레스트는 의사결정나무 분석의 예측 정확도를 높이기 위해 하나의 의사결정나무를 사용하는 대신에 다수의 의사결정나무를 사용해 결과를 예측하는 아상블 학습 기법이다.
6.5.1 앙상블 모델의 개념
앙상블 모델은 주어진 데이터로부터 여러 개의 모델을 학습한 후 결과를 종합하여 알고리즘의 안정성과 정확성을 높이는 방법이다. 이는 데이터에서 표본추출법으로 여러훈련용 데이터 집합을 만들어 각각의 데이터 집합에서 하나의 분류기(최종 모델) 를 만들어 앙상블하는 방법이다.
아상블 기법은 높은 편향 (bias) 로 인한 과소적합과 높은 분산 (variance) 으로 인한 과적합을 최소화하는데 도움이 된다.
배깅(boostrap aggregating)은 일반적인 모델을 만드는데 집중되어있다면, 부스팅은 어려운 문제를 맞추는데 초점이 맞추어져 있다.
부스팅(boosting) - 학습하는 과정에서 오답에 대해 높은 가중치를 부여해서 오답을 잘 맞춘 모델은 최종 모델로 선정한다.
배깅은 병렬로 학습, 부스팅은 순차적으로 학습.
랜덤 포레스트 - 대표적인 배깅 알고리즘 모델이다. 랜덤 포레스트는 일반적으로 성능이 뛰어나고 의사결정나무 여러 개를 사용해 과적합 문제를 피할 수 있다.
6.5.2
배깅(bagging/boostrap aggregating) 알고리즘 원리 : 주어진 자료에서 여러 개의 붓스트랩 자료를 생성하고, 각 표본에 대해 분류기(classifiers)를 생성한 후(모델 학습후) 그 결과를 결합(voting/average) 하여 최종 모델을 만드는 방법이다. 배깅은 각 샘플에서 나타난 결과를 일종의 중앙값으로 맞추기 때문에 과적합을 피할 수 있다.
배깅알고리즘 알아둬야 할점: 배깅은 복원 추출 방법을 사용하기 때문에 같은 데이터가 한 표본에 여러 번 추출될수도 있고, 어떤 데이터는 한번도 추출 되지 않을수 있다.
다은은 adabag 패키지의 bagging 함수로 아이리스 자료를 이용해 배깅 모델을 적합하는 예제다.
## Loading required package: foreach
## Loading required package: doParallel
## Loading required package: iterators
## Loading required package: parallel
data("iris")
summary(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## 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
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
iris 는 5개 변수와 150개의 관측값을 가지고 있다. 그중 Species 는 setosa, versicolor, virginica 세 가지 범주로 분류된다.
2. iris 자료를 훈련용 데이터 80% 와 테스트 데이터 20% 로 분리하기
library(caret)
parts<-createDataPartition(iris$Species, p=0.8)
# training data
iris.train<-iris[parts$Resample1, ]
# test data
iris.test<-iris[-parts$Resample1, ]
table(iris.train$Species)
##
## setosa versicolor virginica
## 40 40 40
##
## setosa versicolor virginica
## 10 10 10
데이터 분할 시 createDataPartition 함수를 사용하여 Species 값의 비율이 원본 데이터와 같게 유지되는 것을 알 수 있다.
3. 훈련용 데이터로 100회 반복(100개 트리 수 사용)으로 배깅 모델 적합하기
bag.fit<-bagging(Species ~., data = iris.train, mfinal = 100)
mfinal = 100 인자는 학습에 사용할 트리 수(반복횟수)를 성정한다.
적합된 모델의 추가적인 정보 확인하기
## [1] "call" "class" "formula" "importance" "prob"
## [6] "samples" "terms" "trees" "votes"
5. 적합된 모델에서 첫 번째 트리 확인하기
## n= 120
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 120 79 versicolor (0.31666667 0.34166667 0.34166667)
## 2) Petal.Length< 2.5 38 0 setosa (1.00000000 0.00000000 0.00000000) *
## 3) Petal.Length>=2.5 82 41 versicolor (0.00000000 0.50000000 0.50000000)
## 6) Petal.Width< 1.75 41 2 versicolor (0.00000000 0.95121951 0.04878049) *
## 7) Petal.Width>=1.75 41 2 virginica (0.00000000 0.04878049 0.95121951) *
# 첫 번째 트리 시각화
plot(bag.fit$trees[[1]])
text(bag.fit$trees[[1]])

첫 번째 트리 모델분석 결과 Petal.Length < 2.6 인경우 setosa 로 분류되고, petal.Length > 2.6 이고 Petal.Width < 1.75 인 경우 versicolor 로 분류되고, petal.Length > 2.6 이고 Petal.Width >= 1.75 인 경우 verginica 로 분류된다.
6. 설명변수 중요도 확인하기
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## 82.36981478 17.57798543 0.05219979 0.00000000
# 중요도를 시각화
barplot(bag.fit$importance[order(bag.fit$importance, decreasing = T)], main = 'Variables Relative Importance')

분석 결과 Petal.Length, Petal.Width 변수 순으로 중요도가 높은 것을 볼 수 있다.
7. 테스트 데이터로 예측을 수행하고, 배깅 모델의 성능 평가하기
bag.pred<-predict(bag.fit, newdata = iris.test)
bag.tb<-table(bag.pred$class, iris.test$Species)
bag.tb
##
## setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 9 1
## virginica 0 1 9
분석 결과 정오분류표를 보면 setosa는 10개 모두, versicolor는 10개중 9개, virginica는 10개 모두 제대로 분류된 것 을 볼수 있다.
정분류율 (Accuracy) 과 오류분율(Error rate) 계산하기
mean(iris.test$Species == bag.pred$class)
## [1] 0.9333333
## [1] 0.06666667
분석 결과 정분류율(Accuracy) 는 0.96 이고 오분류율 (error rate)은 0.03 이다.
6.5.3 부스팅과 분석 예제 with boosting 함수
부스팅 알고리즘: 부스팅은 배깅과 동일하게 복원 임의 샘플링을 하지만 가중치를 부여한다는 차이점이 있다. 부스팅은 순차적으로 학습시키고 학습하는 과정에서 오답에 대해 높은 가중치를 부여하지만, 정답에 대해 낮은 가중치를 부여하기 때문에 오답에 더욱 집중할 수 있다. 부스팅 기법은 정확도가 높게 나오는 반면 이상값(outlier)에 취약하다. 부스팅 모델에는 AdaBoost, XGBoost, GradientBoost 등이 있다.
아다부스트는 이전의 분류기에 의해 잘못 분류된 것을 이어지는 약한 학습기가 수정해줄수있다는 점에서 다양한 상황에 적용할 수 있다.
아다부스트는 잡음이 많은 데이터와 이상값에 취약하지만 과적합에 덜 취약하다.
다음은 adabag 의 boosting 함수로 아이리스 자료를 이용해 부스팅 모델을 적합하는 예제이다.
1. adabag 패키지와 iris 데이터 불로오기
library(adabag)
data('iris')
2. iris 자료를 훈련용 데이터 80% 와 테스트 데이터 20% 로 분리하기
library(caret)
parts<-createDataPartition(iris$Species, p=0.8)
iris.train<-iris[parts$Resample1, ]
iris.test<-iris[-parts$Resample1,]
table(iris.train$Species)
##
## setosa versicolor virginica
## 40 40 40
##
## setosa versicolor virginica
## 10 10 10
3. 훈련용 데이터로 100 회반복(100개 트리 수 사용)으로 부스팅 모델 적합하기
boo.fit<-boosting(Species~., data = iris.train, boos = T, mfinal = 100)
4. 적합된 모델의 추가적인 정보 확인하기
## [1] "call" "class" "formula" "importance" "prob"
## [6] "terms" "trees" "votes" "weights"
##
## setosa versicolor virginica
## 40 40 40
boo.fit 에서 $class 속성 정보의 분할표에서 setosa는 40 개, versicolor는 40개, verginica는 40개로 반응변수가 분류된 것을 볼 수 있다.
5. 적합된 모델에서 100 번째 트리 확인하기
## n= 120
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 120 70 virginica (0.27500000 0.30833333 0.41666667)
## 2) Petal.Length< 2.75 33 0 setosa (1.00000000 0.00000000 0.00000000) *
## 3) Petal.Length>=2.75 87 37 virginica (0.00000000 0.42528736 0.57471264)
## 6) Petal.Width< 1.75 45 8 versicolor (0.00000000 0.82222222 0.17777778)
## 12) Sepal.Width>=2.65 34 1 versicolor (0.00000000 0.97058824 0.02941176) *
## 13) Sepal.Width< 2.65 11 4 virginica (0.00000000 0.36363636 0.63636364) *
## 7) Petal.Width>=1.75 42 0 virginica (0.00000000 0.00000000 1.00000000) *
# 100 번째 모델 시각화
plot(boo.fit$trees[[100]])
text(boo.fit$trees[[100]])

6. 설명변수 중요도 확인하기
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## 51.038538 33.500664 10.170732 5.290067
# 중요도 시각화
barplot(boo.fit$importance[order(boo.fit$importance, decreasing = T)], ylim = c(0,100), main = 'Variables Relative Importance')

분석 결과 petal.Length, Petal.Width 가 중요도가 높은것을 볼 수 있다.
7. 테스트 데이터로 예측을 수행하고 부스팅 모델의 성능 평가하기
boo.pred<-predict(boo.fit, newdata = iris.test)
boo.tb<- table(boo.pred$class, iris.test$Species)
boo.tb
##
## setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 9 0
## virginica 0 1 10
# Accuracy
mean(iris.test$Species == boo.pred$class)
## [1] 0.9666667
# error rate
boo.pred$error
## [1] 0.03333333
분석 결과 정오분류표를 보면 모두 제대로 분류된것을 볼수 있고 정분류율(Accuracy)는 1 이고 오분류율(error rate) 은 0 이다.
6.5.4 랜덤 포레스트와 분석 예제
랜덤포레스트: 의사결정나무 모델의 예측 정확도를 높이기 위해 하나의 의사결정 나무를 사용하는 대신에 다수의 의사결정나무를 사용해 결과를 예측하는 앙상블 학습 기법이다.
랜덤포레스트 알고리즘: 훈련용 데이터셋에서 임의의 샘플을 복원 추출하여 각 샘플에 대해서만 의사결정나무를 만드는 랜덤 포레스트 방식은 배깅과 유사하다. 그러나 노드 내 데이터를 자식노드로 나누는 기준을 정할 때, 전체 예측변수가 아니라 예측변수를 임의로 추출하여 추출된 변수내에서 최적의 분할을 만들어 나가는 방법을 사용한다.
예측방법: 새로운 데이터에 대한 예측에서 목표변수가 분류의 경우는 다수결, 회귀의 경우에는 평균을 취하는 방법을 사용한다.
다음은 randomForest 함수로 stagec 데이터셋을 이용한 랜덤포레스트 모델을 적합하는 예제다.
1. randomForest, rpart 패키지와 stagec 데이터 불러오기
## randomForest 4.7-1
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:dplyr':
##
## combine
## The following object is masked from 'package:ggplot2':
##
## margin
library(rpart)
data(stagec)
str(stagec)
## 'data.frame': 146 obs. of 8 variables:
## $ pgtime : num 6.1 9.4 5.2 3.2 1.9 4.8 5.8 7.3 3.7 15.9 ...
## $ pgstat : int 0 0 1 1 1 0 0 0 1 0 ...
## $ age : int 64 62 59 62 64 69 75 71 73 64 ...
## $ eet : int 2 1 2 2 2 1 2 2 2 2 ...
## $ g2 : num 10.26 NA 9.99 3.57 22.56 ...
## $ grade : int 2 3 3 2 4 3 2 3 3 3 ...
## $ gleason: int 4 8 7 4 8 7 NA 7 6 7 ...
## $ ploidy : Factor w/ 3 levels "diploid","tetraploid",..: 1 3 1 1 2 1 2 3 1 2 ...
##
## diploid tetraploid aneuploid
## 67 68 11
실행 결과 stagec 자료는 총 8개 변수와 146개의 관측값이 존재하는 것을 볼 수 있다. 목표변수는 diploid, tetraploid, aneuploid 세 가지 범주로 분류된다. 목표변수의 분할표에서 diploid 67개, tetraploid 68개, aneuploid 11개인 것을 알 수 있다.
2. 결측값, 중복데이터 확인 및 제거
## pgtime pgstat age eet g2 grade gleason ploidy
## 0 0 0 2 7 0 3 0
## [1] 12
# NA 제거
stagec2<-stagec[complete.cases(stagec), ]
colSums(is.na(stagec2))
## pgtime pgstat age eet g2 grade gleason ploidy
## 0 0 0 0 0 0 0 0
## [1] 134
## [1] 0
분석 결과 결측값이 12개 있어서 모두 제거 하였고 중복데이터는 존재하지 않는다.
3. 데이터셋을 훈련용 데이터 80%와 테스트 데이터 20%로 분리하기
library(caret)
parts<-createDataPartition(stagec2$ploidy, p = 0.8 )
stagec.train<- stagec2[parts$Resample1, ]
stagec.test<- stagec2[-parts$Resample1, ]
table(stagec.train$ploidy)
##
## diploid tetraploid aneuploid
## 52 52 4
table(stagec.test$ploidy)
##
## diploid tetraploid aneuploid
## 13 12 1
4. 훈련용 데이터로 랜덤 포레스트 모델을 생성하기
rf.fit<-randomForest(ploidy ~., data = stagec.train, ntree = 500, proximity = T)
rf.fit
##
## Call:
## randomForest(formula = ploidy ~ ., data = stagec.train, ntree = 500, proximity = T)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 2
##
## OOB estimate of error rate: 5.56%
## Confusion matrix:
## diploid tetraploid aneuploid class.error
## diploid 50 1 1 0.03846154
## tetraploid 0 52 0 0.00000000
## aneuploid 3 1 0 1.00000000
실행 결과 정오분류표와 오분류율에 대한 OOB 추정치를 제공한다. 랜덤 포레스트는 붓스트랩 샘플 과정에서 제외된 OOB 자료를 사용하여 검증을 실시할 수 이싿.
5. plot 함수로 반응변수 범주별 정오분류율 시각화하기

검은색이 전체 정오분류율, 빨강이 diploid, 연두가 tetraploid, 하늘이 aneuploid 이다.
6. 설명변수 중요도 확인하기
importance(rf.fit)[order(importance(rf.fit), decreasing = T)]
## [1] 39.272219 5.100241 3.664813 2.818158 2.696932 1.561626 1.095731
# 중요도 시각화
varImpPlot(rf.fit)

분석 결과 g2, pgtime, age, gleason, pgstat, grade, eet 변수 순으로 중요도가 높은 것을 볼 수 있다.
7. 테스트 데이터로 예측을 수행하고, 랜덤 포레스트 모델의 성능 평가하기
rf.pred<-predict(rf.fit, newdata = stagec.test)
fr.tb<-table(rf.pred, stagec.test$ploidy)
fr.tb
##
## rf.pred diploid tetraploid aneuploid
## diploid 13 0 1
## tetraploid 0 12 0
## aneuploid 0 0 0
# Accuracy
mean(rf.pred == stagec.test$ploidy)
## [1] 0.9615385
# Error rate
(1-sum(diag(fr.tb)/sum(fr.tb)))
## [1] 0.03846154
분석 결과 정오분류표를 보면 diploid는 13개 모두, tetraploid는 12개 모두, aneuploid는 1개 중 0개가 제대로 분류된 것을 볼 수 있다. 정분류율(Accuracy)는 약 0.962 이고 오분류율(error rate)은 약 0.038이다.
6.6 서포트 벡터 머신
서포트 벡터 머신 모델은 데이터를 선형으로 분리하는 최적의 선형 결정 경계를 찾는 알고리즘이다.
마진은 두 데이터 군과 결정 경계와 떨어져 있는 정도를 의미한다.
서포트 벡터 머신은 분류와 수치 예측 문제에 모두 활용할 수 있으며 분류 성능이 우수하면서 과적합이 잘되지 않는다.
커널트릭은 주어진 데이터를 적절한 고차원으로 옮긴 뒤, 변환된 차원에서 데이터를 잘 분류할 수 있는 초평면을 찾는다.
ksvm 함수는 서포트 벡터 머신 모델을 생성한다.
적합된 모델에서 추가적인 정보는 ls()함수로 확인 가능하다.
cost는 얼마나 많은 데이터 샘플들이 다른 클래스에 놓이는 것을 허용하는지 결정한다.
gamma는 하나의 데이터 샘플이 영향력을 행사하는 거리를 결정한다.
6.6.1 서포트 벡터 머신 모델의 개념
서포트 벡터 머신는 서로 다른 분류에 속하는 데이터 간에 간격(마진) 이최대가 되는 선 (초평면) 을 찾아서 이를 기준으로 데이터를 분류하는 모델이다. 즉 SVM은 데이터를 선형으로 분리하는 최적의 선형 결정 경계를 찾는 알고리즘이다.
마진은 두 데이터 범주와 결정 경계와 떨어져 있는 정도를 의미한다.
서포트 벡터 알고리즘: 두 범주 간의 데이터를 같은 간격으로 그리고 최대로 멀리 떨어진 선 또는 평면을 찾는다. 두범주간의 데이터를 나누는 직선 혹은 평면은 여러 개가 있을 수 있지만 현재의 훈련용 데이터가 아닌, 미래의 데이터를 분류 예측하는데 최대한 일반화하여 분류를 이끌어 낼수 있는 최대여백 초평면 을 찾고자 하는 것이다. 여기서 이 경계선과 가장 가까운 각 분류에 속한 점들을 서포트 벡터 라고 한다.
때로는 곡선 형태나 더 복잡한 형태의 비선형 분류 평면으로 데이터를 분류해야 할 경우도 있는데, 이런경우에는 커널트릭이라는 기법으로 해결한다.
커널트릭: 주어진 데이터를 적절한 고차원으로 옮긴뒤, 변환된 차원에서 데이터를 잘 분류할 수 있는 초평면을 찾는다. 즉 커널 기법은 주어진 데이터를 고차원 특징 공간으로 사상해주는 것이다. 커널 기법에서 데이터를 고차원으로 변환하는 대신에 고차원 벡터 간 내적 계산을 수행했을 때와 유사한 값을 반환하는 커널 함수를 사용한다.
대표적인 커널 함수: 다항 커널, 가우시안 커널, 레이디얼 베이스 함수 커널, 시그모이드 커널 등이 있는데 일반적으로 가우시안 커널의 성능이 가장 우수해 가장많이 사용된다.
서포트벡터 장점
범주분류나 수치 예측 문제에 모두 활용이 가능하다.
노이즈 데이터에 영향을 많이 받지 않고, 과적합이 잘 일어나지 않는다.
일반적으로 분류 문제에서 다른 알고리즘보다 성능이 높은 것으로 알려져 있다.
분류 경계가 복잡한 비선형 문제일 경우 타 기법 대비 성능이 우수하다.
서포트벡터 단점
최적 분류를 위해 커널 함수와 매개변수 등에 대한 반복적인 조합 테스트가 필요하다.
입력 데이터가 대량이거나 변수가 많은 경우 오랜 훈련시간이 필요하다.
배경이 되는 이론과 알고리즘 구현시 타 기법에 비하여 상대적으로 난해한 면이 있다.
결과 해석이나 설명 등에 있어 어려움이 있다.
서포트 벡터 모델을 위한 패키지: kernlab, e1071
6.6.2 서포트 벡터 머신 분석 예제 with ksvm 함수
1. kernlab 패키지와 iris 데이터 불러오기
##
## Attaching package: 'kernlab'
## The following object is masked from 'package:scales':
##
## alpha
## The following object is masked from 'package:modeltools':
##
## prior
## The following object is masked from 'package:ggplot2':
##
## alpha
2. iris 자료를 훈련용 데이터 80%와 테스트 데이터 20%로 분리하기
library(caret)
parts<-createDataPartition(iris$Species, p = 0.8)
# training data
iris.train<-iris[parts$Resample1, ]
# test data
iris.test<-iris[-parts$Resample1, ]
table(iris.train$Species)
##
## setosa versicolor virginica
## 40 40 40
##
## setosa versicolor virginica
## 10 10 10
3. 훈련용 데이터로 서포트 벡터 머신 모델 생성하기
svm.fit<-ksvm(Species ~., data = iris.train)
svm.fit
## Support Vector Machine object of class "ksvm"
##
## SV type: C-svc (classification)
## parameter : cost C = 1
##
## Gaussian Radial Basis kernel function.
## Hyperparameter : sigma = 0.847876968601768
##
## Number of Support Vectors : 54
##
## Objective Function Value : -4.3672 -5.17 -19.1235
## Training error : 0.033333
서포트 벡터 머신 모델 생성에 이용한 ksvm() 함수는 사용할 커널에 대한 옵션을 지정하지 않는경우 레이디얼 베이스 함수 커널을 이용한다.
4. 테스트 데이터로 예측을 수행하고, 서포트 벡터 머신 모델의 성능 평가하기
svm.pred<-predict(svm.fit, newdata = iris.test)
head(svm.pred)
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
svm.tb<-table(svm.pred, iris.test$Species)
svm.tb
##
## svm.pred setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 10 0
## virginica 0 0 10
# Accuracy
mean(svm.pred == iris.test$Species)
## [1] 1
# Error rate
(1-sum(diag(svm.tb))/sum(svm.tb))
## [1] 0
분석 결과 versicolor의 10 중 9개는 제대로 분류 되었고 나머지 setosa, virginica 도 모두 제대로 분류 되었다. 정분류율은 0.967 이고 오분류율은 0.033 이다.
6.6.3 서포트 벡터 머신 분석 예제 with svm() 함수
다음은 e1071 패키지 svm 함수로 아이리스 자료를 이용해 서포트 벡터 머신 모델을 생성 하는 예제다.
1. 훈련용 데이터로 서포트 벡터 머신 모델 생성하기
##
## Attaching package: 'e1071'
## The following objects are masked from 'package:PerformanceAnalytics':
##
## kurtosis, skewness
svm.fit<-svm(Species ~., data = iris.train)
svm.fit
##
## Call:
## svm(formula = Species ~ ., data = iris.train)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: radial
## cost: 1
##
## Number of Support Vectors: 46
2. 적합된 모델 추가적인 정보 확인하기
## [1] "call" "coef0" "coefs" "compprob"
## [5] "cost" "decision.values" "degree" "epsilon"
## [9] "fitted" "gamma" "index" "kernel"
## [13] "labels" "levels" "na.action" "nclasses"
## [17] "nSV" "nu" "probA" "probB"
## [21] "rho" "scaled" "sigma" "sparse"
## [25] "SV" "terms" "tot.nSV" "type"
## [29] "x.scale" "y.scale"
## [1] 1
## [1] 0.25
SVM 의 기본 매개변수인 cost(C) 와 gamma
데이터에 이상값이 관측된 데이터가 있을때, 이런 데이터를 완변하게 분리해내는 것은 어려운것이다. 이를 해결하기 위하여 약간의 오류를 허용하게되는데 이 허용정도(얼마나 데이터 샘플이 다른 클래스에 놓이는 것을 허용하는지 결정)를 cost라고 한다. 어느 정도의 cost를 허용함(cost를 낮게 봄)으로서 모델을 새로운 데이터에 적합할때 좋은 성능을 보일수 있지만 cost를 높게 보면 트레이닝 데이터를 잘 분류하겠지만 새로운 데이터 적합에서 좋은 성능을 보일기 힘들다. 즉 cost 값이 너무 작으면 과서적합이 될 가능성이 커지고, c 값이 너무 높으면 과적합이 될 가능 성이 커지게 된다.
gamma는 하나의 데이터 샘플이 영향력을 행사하는 거리를 결정한다.gamma 값이 클수록 한데이터 포인터가 영향력을 행사하는 거리는 짧ㅅ아지는 반면 gamma값이 작을수록 한 데이터 포인터가 영향력을 행사하는 거리는 커진다. gamma 값이 커질수록 결정 경계가 점점 더 구불구불해지는데 이는 즉, gamma 매개변수는 결정 경계의 곡률을 조정한다고 말할수 있다. gamma 값이 너무 작으면 과소적합될 가능성이 크고, gamma 값이 너무 높으면 과적합의 위험이 있다.
3. 테스트 데이터로 예측을 수행하고, 서포트 벡터 머신 모델의 성능 평가하기
# predict the test sets
svm.pred<-predict(svm.fit, iris.test)
# confusion matrix
svm.tb<-table(svm.pred, iris.test$Species)
svm.tb
##
## svm.pred setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 10 0
## virginica 0 0 10
# Accuracy
mean(svm.pred == iris.test$Species)
## [1] 1
# Error Rate
(1-sum(diag(svm.tb))/sum(svm.tb))
## [1] 0
분석결과 virginica 는 10개 중 8개를 제대로 분류하였고 나머지 setosa 와 versicolor 는 모두 제대로 분류 되었다. 정분류율은 0.933 이고 오분류율은 약 0.066 이다.
나이브 베이즈
나이브 베이즈 모델은 목표변수의 범주를 학습시키기 위해 통계학의 베이즈 확률 추정에 기반을 둔 확률 모델인 베이즈 정리 또는 베이즈 룰을 사용한다.
베이즈 정리는 두 확률변수의 사전 확률과 사후 확률 사이의 관계를 나타내는 정리다.
나이브 베이즈의 확률적 추론 방법은 어떤 가설의 확률을 평가하기 위해 임의적으로 사전 확률을 먼저 정하고, 관찰된 데이터를 기반으로 하는 가능도를 계산해서 처음에 설정한 임의적 확률을 보정하는 방법이다. 이때 베이즈 정리는 이러한 확률을 해석하는데 있어 핵심적인 개념을 제공한다.
naiveBayes 함수는 나이브 베이즈 모델을 생성한다.
6.7.1 나이브 베이즈 모델의 개념
나이브 베이즈 모델은 목표변수의 범주를 학습시키기 위해 통계학의 베이즈 확률 추정에 기반을 둔 확률 모델인 베이즈 정리 또는 베이즈 룰을 사용한다.
나이브 기법 장, 단점
장점:
개념이 단순하고 계산이 빠르다
고차원의 데이터셋에 적합하다
데이터에 노이즈 및 결측값이 포함되어 있어도 잘 동작한다
단점
범주 분류 문제에 적합하지만, 예측된 범주의 확률값을 활용해야 할 경우에는 적합하지 않다.
독립변수들이 범주 형태가 아닌 수치 형태일 경우에는 정확성이 떨어진다.
독립변수가 서로 독립적이고, 중요도가 같다는 가정이 위배되는 경우에 오류가 발생할 수 있다.
6.7.2 나이브 베이즈 분석 with naiveBayes 함수
다음은 e1071 패키지의 naiveBayes 함수로 mlbench 패키지의 HouseVotes84 자료를 이용해 나이브 베이즈 모델을 생성하는 예제다.
1. e1071, mlbench 패키지와 HouseVote84 데이터 불로오기
library(e1071)
library(mlbench)
data(HouseVotes84, package = 'mlbench')
str(HouseVotes84)
## 'data.frame': 435 obs. of 17 variables:
## $ Class: Factor w/ 2 levels "democrat","republican": 2 2 1 1 1 1 1 2 2 1 ...
## $ V1 : Factor w/ 2 levels "n","y": 1 1 NA 1 2 1 1 1 1 2 ...
## $ V2 : Factor w/ 2 levels "n","y": 2 2 2 2 2 2 2 2 2 2 ...
## $ V3 : Factor w/ 2 levels "n","y": 1 1 2 2 2 2 1 1 1 2 ...
## $ V4 : Factor w/ 2 levels "n","y": 2 2 NA 1 1 1 2 2 2 1 ...
## $ V5 : Factor w/ 2 levels "n","y": 2 2 2 NA 2 2 2 2 2 1 ...
## $ V6 : Factor w/ 2 levels "n","y": 2 2 2 2 2 2 2 2 2 1 ...
## $ V7 : Factor w/ 2 levels "n","y": 1 1 1 1 1 1 1 1 1 2 ...
## $ V8 : Factor w/ 2 levels "n","y": 1 1 1 1 1 1 1 1 1 2 ...
## $ V9 : Factor w/ 2 levels "n","y": 1 1 1 1 1 1 1 1 1 2 ...
## $ V10 : Factor w/ 2 levels "n","y": 2 1 1 1 1 1 1 1 1 1 ...
## $ V11 : Factor w/ 2 levels "n","y": NA 1 2 2 2 1 1 1 1 1 ...
## $ V12 : Factor w/ 2 levels "n","y": 2 2 1 1 NA 1 1 1 2 1 ...
## $ V13 : Factor w/ 2 levels "n","y": 2 2 2 2 2 2 NA 2 2 1 ...
## $ V14 : Factor w/ 2 levels "n","y": 2 2 2 1 2 2 2 2 2 1 ...
## $ V15 : Factor w/ 2 levels "n","y": 1 1 1 1 2 2 2 NA 1 NA ...
## $ V16 : Factor w/ 2 levels "n","y": 2 NA 1 2 2 2 2 2 2 NA ...
table(HouseVotes84$Class)
##
## democrat republican
## 267 168
실행 결과 HouseVotes84 데이터는 17개 변수와 435개의 관측값이 존재하는 것을 볼 수 있다. 미국의 하원의원 435 명 중에 267명이 민주당이고 168명이 공화당이다.
HouseVotes84 데이터 요약
## Class V1 V2 V3 V4 V5
## democrat :267 n :236 n :192 n :171 n :247 n :208
## republican:168 y :187 y :195 y :253 y :177 y :212
## NA's: 12 NA's: 48 NA's: 11 NA's: 11 NA's: 15
## V6 V7 V8 V9 V10 V11 V12
## n :152 n :182 n :178 n :206 n :212 n :264 n :233
## y :272 y :239 y :242 y :207 y :216 y :150 y :171
## NA's: 11 NA's: 14 NA's: 15 NA's: 22 NA's: 7 NA's: 21 NA's: 31
## V13 V14 V15 V16
## n :201 n :170 n :233 n : 62
## y :209 y :248 y :174 y :269
## NA's: 25 NA's: 17 NA's: 28 NA's:104
colSums(is.na(HouseVotes84))
## Class V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12
## 0 12 48 11 11 15 11 14 15 22 7 21 31
## V13 V14 V15 V16
## 25 17 28 104
## [1] 392
분석결과 반응변수/목표변수 Class 를 제외한 나머지 변수들은 모두 결측값이 존재한다는 것을 알 수 있다.
3. HouseVotes84 자료를 훈련용 데이터 80%와 테스트 데이터 20%로 분리하기
library(caret)
parts<-createDataPartition(HouseVotes84$Class, p=0.8)
data.train<-HouseVotes84[parts$Resample1, ]
data.test<-HouseVotes84[-parts$Resample1, ]
table(data.train$Class)
##
## democrat republican
## 214 135
##
## democrat republican
## 53 33
4. 훈련용 데이터로 나이브 베이즈 모델을 생성하기
nai.fit<-naiveBayes(Class ~., data = data.train)
5. 테스트 데이터로 예측을 수행하고, 나이브 베이즈 모델의 성능 평가하기
nai.pred<-predict(nai.fit, newdata = data.test, type = 'class')
nai.tb<-table(nai.pred, data.test$Class)
nai.tb
##
## nai.pred democrat republican
## democrat 47 3
## republican 6 30
# Accuracy
mean(nai.pred == data.test$Class)
## [1] 0.8953488
# Error rate
(1-sum(diag(nai.tb))/sum(nai.tb))
## [1] 0.1046512
분석결과 democrat의 53개중 45개가 제대로 분류되었고 republican의 33개중 30개가 제대로 분류되었다. 정분류율은 약 0.872 이고 오분류율은 약 0.128 이다.
6.8 k-최근접 이웃
k-최근접 이웃 모델은 목표변수의 범주를 알지 못하는 데이터 셋의 분류를 위해해당 데이터셋과 가장 유사한 k개의 주변 데이터셋을 수집하고, k개의 데이터셋이 가장 많이 속해 있는 범주로 지정하는 방식으로 분류 예측을 하는 기법이다.
데이터 간의 유사성을 측정하는 방식은 일반적으로 두 점간의 유클리드 거리의 역수를 사용하거나 피어슨 상관계수를 이용하여 계산한다.
knn 함수는 k-최근접 이웃 모델을 생성한다.
train.kknn() 함수는 최적의 k 값을 k-fold 교차검증 방법으로 찾는다.
6.8.1 k-최근접 이웃 모델의 개념
k-최근접 이웃 모델은 목표변수의 범주를 알지 못하는 데이터 셋의 분류를 위해해당 데이터셋과 가장 유사한 k개의 주변 데이터셋을 수집하고, k개의 데이터셋이 가장 많이 속해 있는 범주로 지정하는 방식으로 분류 예측을 하는 기법이다.
해당 데이터 점과 유사한 k개의 주변 데이터 점에서 다수결의 원칙에 따라 새로운 범주를 결정하는 방식이 k-최근접 이웃 기법이다
k-최근접 이웃 기법에서 적절한 k값을 정하는 것이 매우 중요하다. 여러가지 k 값을 설정해보면서 반복적으로 테스트하여 최적의 분류 성능을 보이는 k 값으로 최종적으로 정하면 된다. 다만 k 값은 관측값^0.5 보다는 작은 것이 좋다고 알려져있다.
k-최근접 이웃 기법의 장,단점 비교
장점
알고리즘이 이해하기 쉽고 직관적이다.
데이터셋의 확률분포 등에 대한 가정이 필요하지 않다.
사전 모델 성정 및 모수 추정이 필요없다.
훈련시간이 빠르다.
단점
k 값에 대한 명확한 기준이 없어 시행착오적 접근이 필요하다.
특정한 가설이나 모델 없이 주어진 데이터를 통해 범주의 분류 결과만 판단함으로 분석을 통한 통찰력을 얻기 어렵다.
새로운 데이터가 주어질 떄마다 모든 데이터와의 유사도를 계산해야함으로 그만큼 시간소요가 많다. 이런 특성 때문에 게으른 학습으로 불린다.
데이터셋의 모든 데이터들과 거리 계산을 위해 메인 메모리에 가져와야 함으로 많은 메모리가 필요하다
6.8.2 k-최근접 이웃 분석 예제 with knn 함수
다음은 class 패키지의 knn 함수로 아이리스 자료를 이용해 k-최근접 이웃 알고리즘 모델을 생성하는 예제다.
1. class 패키지와 iris 데이터 불러오기
##
## Attaching package: 'class'
## The following object is masked from 'package:reshape':
##
## condense
2. iris 자료를 훈련용 데이터 80%와 트스트 데이터 20%로 분리하기
library(caret)
# partition
parts<-createDataPartition(iris$Species, p = 0.8)
# training data
data.train<-iris[parts$Resample1, ]
# test data
data.test<-iris[-parts$Resample1, ]
table(data.train$Species)
##
## setosa versicolor virginica
## 40 40 40
##
## setosa versicolor virginica
## 10 10 10
3. k = 1 부터 k= 10 사이의 범위에서 정분류율 계산하기
library(foreach)
knn.k<-c(1,2,3,4,5,6,7,8,9,10)
knn_result<-foreach(k = knn.k, .combine = rbind) %do% {
knn.pred<-knn(data.train[,1:4], data.test[,1:4],
data.train$Species, k = k , prob = T)
acc.val<-mean(knn.pred == data.test$Species)
return(data.frame(k = k, acc = acc.val))
}
knn_result
## k acc
## 1 1 0.9333333
## 2 2 0.9333333
## 3 3 0.9666667
## 4 4 0.9666667
## 5 5 0.9666667
## 6 6 0.9666667
## 7 7 1.0000000
## 8 8 1.0000000
## 9 9 1.0000000
## 10 10 1.0000000
분석 결과 k = 3 인 경우 정분류율이 96.67% 이므로 k 값을 3 으로 결정한다.
4. 준비된 데이터셋으로 k-최근접 이웃 모델 생성하기
knn_model<-knn(data.train[,1:4], data.test[,1:4], data.train$Species, k = 3, prob = TRUE)
5. 테스트 데이터로 예측을 수행하고, k-최근접 이웃 모델의 성능 평가하기
knn.tb<-table(knn.pred, data.test$Species)
knn.tb
##
## knn.pred setosa versicolor virginica
## setosa 10 0 0
## versicolor 0 10 0
## virginica 0 0 10
# Accuracy
mean(knn.pred == data.test$Species)
## [1] 1
# Error rate
(1-sum(diag(knn.tb))/sum(knn.tb))
## [1] 0
분석 결과 정오류분류표 (confusion matrix)를 보면 setosa는 10개, versicolor는 10중 9개, virginica는 모두 제대로 분류된 것을 볼 수 있다. 정분류율은 약 0.967이고 오분류율은 약 0.033 이다.
6.8.3 k-최근접 이웃 분석 예제 kknn 함수
다음은 kknn패키지의 kknn 함수로 BreastCancer 자료를 이용해 k-최근접 이웃 모델을 생성하는 예제다.
1. kknn 패키지와 BreastCancer 데이터 불러오기
##
## Attaching package: 'kknn'
## The following object is masked from 'package:caret':
##
## contr.dummy
library(mlbench)
data("BreastCancer")
2. 결측값 확인 및 제거하기
# 각 컬럼의 결측값
colSums(is.na(BreastCancer))
## Id Cl.thickness Cell.size Cell.shape Marg.adhesion
## 0 0 0 0 0
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 0 16 0 0 0
## Class
## 0
# 토탈 결측값
sum(is.na(BreastCancer))
## [1] 16
# 결측값 제거
BreastCancer2<-BreastCancer[complete.cases(BreastCancer), ]
# 중복값 탐색
sum(duplicated(BreastCancer2))
## [1] 8
# 중복값 제거
BreastCancer2<-BreastCancer2[!duplicated(BreastCancer2),]
분석 결과 16개의 결측치와 8개의 중복값이 존재하였고 결측치와 중복값을 제거하였다.
3. BreastCancer 데이터로 훈련용 데이터 80%, 테스트 데이터 20%로 분리하기
library(caret)
parts<-createDataPartition(BreastCancer2$Class, p = 0.8)
data.train<-BreastCancer2[parts$Resample1, ]
data.test<-BreastCancer2[-parts$Resample1, ]
table(data.train$Class)
##
## benign malignant
## 352 189
##
## benign malignant
## 87 47
4. 훈련용 데이터셋을 이용해 최적의 k 값 확인하기-cross validation to get the best k.
knn.tr<-train.kknn(Class ~., data.train[,-1], kmax = 10, distance = 1, kernel = 'rectangular')
# missclassification errors(the lower, the better)
knn.tr$MISCLASS
## rectangular
## 1 0.03327172
## 2 0.04621072
## 3 0.03327172
## 4 0.04066543
## 5 0.03881701
## 6 0.04066543
## 7 0.03881701
## 8 0.03881701
## 9 0.03696858
## 10 0.04066543
## $kernel
## [1] "rectangular"
##
## $k
## [1] 1
분석결과 k = 9 인경우 오분류 에러 (missclassification errors) 값이 약 0.033으로 가장 낮게 나온 것을 볼 수 있다.train.kknn 함수는 k 값을 k fold 교차검증 방법으로 찾는다.
5. 준비된 k 와 데이터를 이용하여 k-최근접 이웃 모델 생성하기- builing model
# ID 를 제외한 변수들을 포함
kkn.fit<-kknn(Class ~., data.train[,-1], data.test[,-1], k=9, distance = 1, kernel = 'rectangular')
summary(kkn.fit)
##
## Call:
## kknn(formula = Class ~ ., train = data.train[, -1], test = data.test[, -1], k = 9, distance = 1, kernel = "rectangular")
##
## Response: "nominal"
## fit prob.benign prob.malignant
## 1 benign 1.0000000 0.0000000
## 2 benign 1.0000000 0.0000000
## 3 benign 1.0000000 0.0000000
## 4 benign 1.0000000 0.0000000
## 5 benign 1.0000000 0.0000000
## 6 benign 1.0000000 0.0000000
## 7 benign 1.0000000 0.0000000
## 8 benign 1.0000000 0.0000000
## 9 benign 1.0000000 0.0000000
## 10 benign 1.0000000 0.0000000
## 11 malignant 0.0000000 1.0000000
## 12 benign 1.0000000 0.0000000
## 13 malignant 0.0000000 1.0000000
## 14 benign 1.0000000 0.0000000
## 15 malignant 0.3333333 0.6666667
## 16 malignant 0.3333333 0.6666667
## 17 benign 1.0000000 0.0000000
## 18 malignant 0.2222222 0.7777778
## 19 malignant 0.0000000 1.0000000
## 20 malignant 0.4444444 0.5555556
## 21 malignant 0.1111111 0.8888889
## 22 benign 1.0000000 0.0000000
## 23 benign 1.0000000 0.0000000
## 24 benign 1.0000000 0.0000000
## 25 benign 1.0000000 0.0000000
## 26 malignant 0.0000000 1.0000000
## 27 benign 0.7777778 0.2222222
## 28 malignant 0.0000000 1.0000000
## 29 malignant 0.0000000 1.0000000
## 30 malignant 0.0000000 1.0000000
## 31 malignant 0.1111111 0.8888889
## 32 benign 1.0000000 0.0000000
## 33 benign 1.0000000 0.0000000
## 34 malignant 0.1111111 0.8888889
## 35 benign 1.0000000 0.0000000
## 36 benign 1.0000000 0.0000000
## 37 malignant 0.0000000 1.0000000
## 38 benign 1.0000000 0.0000000
## 39 malignant 0.0000000 1.0000000
## 40 malignant 0.0000000 1.0000000
## 41 benign 1.0000000 0.0000000
## 42 malignant 0.0000000 1.0000000
## 43 benign 1.0000000 0.0000000
## 44 malignant 0.0000000 1.0000000
## 45 benign 1.0000000 0.0000000
## 46 malignant 0.0000000 1.0000000
## 47 benign 1.0000000 0.0000000
## 48 malignant 0.0000000 1.0000000
## 49 benign 1.0000000 0.0000000
## 50 malignant 0.0000000 1.0000000
## 51 benign 1.0000000 0.0000000
## 52 benign 1.0000000 0.0000000
## 53 malignant 0.0000000 1.0000000
## 54 benign 1.0000000 0.0000000
## 55 benign 1.0000000 0.0000000
## 56 benign 1.0000000 0.0000000
## 57 malignant 0.0000000 1.0000000
## 58 malignant 0.0000000 1.0000000
## 59 benign 1.0000000 0.0000000
## 60 malignant 0.1111111 0.8888889
## 61 malignant 0.0000000 1.0000000
## 62 malignant 0.0000000 1.0000000
## 63 malignant 0.0000000 1.0000000
## 64 benign 1.0000000 0.0000000
## 65 malignant 0.0000000 1.0000000
## 66 benign 1.0000000 0.0000000
## 67 malignant 0.0000000 1.0000000
## 68 benign 1.0000000 0.0000000
## 69 benign 1.0000000 0.0000000
## 70 benign 1.0000000 0.0000000
## 71 malignant 0.1111111 0.8888889
## 72 benign 1.0000000 0.0000000
## 73 malignant 0.1111111 0.8888889
## 74 benign 1.0000000 0.0000000
## 75 benign 1.0000000 0.0000000
## 76 benign 1.0000000 0.0000000
## 77 benign 1.0000000 0.0000000
## 78 malignant 0.0000000 1.0000000
## 79 malignant 0.0000000 1.0000000
## 80 benign 1.0000000 0.0000000
## 81 benign 1.0000000 0.0000000
## 82 benign 1.0000000 0.0000000
## 83 malignant 0.1111111 0.8888889
## 84 malignant 0.1111111 0.8888889
## 85 benign 1.0000000 0.0000000
## 86 benign 1.0000000 0.0000000
## 87 benign 1.0000000 0.0000000
## 88 benign 1.0000000 0.0000000
## 89 benign 1.0000000 0.0000000
## 90 benign 1.0000000 0.0000000
## 91 benign 1.0000000 0.0000000
## 92 malignant 0.0000000 1.0000000
## 93 malignant 0.0000000 1.0000000
## 94 malignant 0.0000000 1.0000000
## 95 malignant 0.0000000 1.0000000
## 96 benign 1.0000000 0.0000000
## 97 benign 1.0000000 0.0000000
## 98 benign 1.0000000 0.0000000
## 99 benign 1.0000000 0.0000000
## 100 benign 1.0000000 0.0000000
## 101 benign 1.0000000 0.0000000
## 102 benign 1.0000000 0.0000000
## 103 benign 1.0000000 0.0000000
## 104 benign 1.0000000 0.0000000
## 105 malignant 0.0000000 1.0000000
## 106 benign 1.0000000 0.0000000
## 107 benign 1.0000000 0.0000000
## 108 benign 1.0000000 0.0000000
## 109 benign 1.0000000 0.0000000
## 110 benign 1.0000000 0.0000000
## 111 malignant 0.0000000 1.0000000
## 112 malignant 0.0000000 1.0000000
## 113 malignant 0.0000000 1.0000000
## 114 benign 1.0000000 0.0000000
## 115 benign 1.0000000 0.0000000
## 116 benign 1.0000000 0.0000000
## 117 benign 1.0000000 0.0000000
## 118 malignant 0.0000000 1.0000000
## 119 benign 1.0000000 0.0000000
## 120 benign 1.0000000 0.0000000
## 121 benign 0.7777778 0.2222222
## 122 benign 1.0000000 0.0000000
## 123 benign 1.0000000 0.0000000
## 124 benign 1.0000000 0.0000000
## 125 benign 1.0000000 0.0000000
## 126 benign 1.0000000 0.0000000
## 127 benign 1.0000000 0.0000000
## 128 benign 1.0000000 0.0000000
## 129 benign 1.0000000 0.0000000
## 130 benign 1.0000000 0.0000000
## 131 benign 1.0000000 0.0000000
## 132 malignant 0.0000000 1.0000000
## 133 benign 1.0000000 0.0000000
## 134 malignant 0.1111111 0.8888889
6. 적합된 모델의 추가적인 정보 확인하기
## [1] "C" "call" "CL" "D"
## [5] "distance" "fitted.values" "prob" "response"
## [9] "terms" "W"
7. 테스트 데이터로 예측을 수행하고, k-최근접 이웃 모델의 성능 평가하기
kkn.tb<-table(kkn.fit$fitted.values, data.test$Class)
kkn.tb
##
## benign malignant
## benign 85 1
## malignant 2 46
# Accuracy
mean(kkn.fit$fitted.values == data.test$Class)
## [1] 0.9776119
# Error rate
(1-sum(diag(kkn.tb))/sum(kkn.tb))
## [1] 0.02238806
분석 결과 정오분류표를 보면 benign 의 87개 중 87개를 모두 제대로 분류하였고 malignant는 47개 중 42개를 제대로 분류하였다. kknn모델의 정분류율은 약 0.962 이고 오분류율은 약0.037 이다.
LS0tCnRpdGxlOiAiQ2hhcHRlciA2IOu2hOulmOu2hOyEnSIKYXV0aG9yOiAiQ2h1bmppZSBOYW4iCmRhdGU6ICIyMDIyLTEwLTE3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAojIyA2LjEg642w7J207YSwIOuniOydtOuLnQoKIyMgNi4xLjEg642w7J207YSwIOuniOydtOuLiOydmCDqsJzrhZAKCuuNsOydtO2EsCDrp4jsnbTri53snYAg64yA7Jqp65+JIOuNsOydtO2EsOyXkOyEnCDsib3qsowg65Oc65+s64KY7KeAIOyViuuKlCDsnKDsmqntlZwg7KCV67O066W8IOywvuyVhOuCtOuKlCDqs7zsoJXsnbTri6QuIOuMgOyaqeufiSDrjbDsnbTthLDsl5DshJwg7J2Y66+4IOyeiOuKlCDtjKjthLTsnYQg7YyM7JWF7ZWY6rGw64KYIOyYiOy4oe2VmOyXrCDsnZjsgqzqsrDsoJXsl5AK7Zmc7Jqp7ZWY64qUIOuwqeuyleydtOudvCDtlaAg7IiYIOyeiOuLpC4gCgrthrXqs4Qg67aE7ISd7J2AIOqwgOyEpOyXkCDrlLDrnbwg67aE7ISd7J2064KYIOqygOymneydhCDtlZjsp4Drp4wsIOuNsOydtO2EsCDrp4jsnbTri53snYAg64uk7JaR7ZWcIOyImOumrCDslYzqs6DrpqzsppjsnYQg7J207Jqp7ZW0IOuNsOydtO2EsOyXkOyEnCDsnZjrr7jsnojripQg7KCV67O066W8IOywvuyVhOuCtOuKlCDrsKnrspXsnYQg7Ya17Lmt7ZWc64ukLgoK7KeA64+E7ZWZ7Iq17KSRIOu2hOulmOuKlCDrjbDsnbTthLDrpbwg7Jes65+sIOqwkiDspJEg7ZWY64KY66GcIOu2hOulmO2VmOuKlCDrspTso7wg7YOA6rKf7Jy866GcLCDtmozqt4DrtoTshJ3snYAg642w7J207YSwIOqwkuydhCDsmIjsuKEsIOy2lOygle2VmOuKlCDsiJjsuZgg7YOA6rKf7Jy866GcIOuCmO2DgOuCuOuLpC4gCgrruYTsp4Drj4TtlZnsirXsnYAg642w7J207YSwIOuCtOyXkOyEnCDqtIDqs4TsmYAg7Jyg7IKs7ISx7J2EIO2MjOyVhe2VmOuKlCDqsoPsl5Ag66qp7KCB7J20IOyeiOuLpC4g7J2064qUIOyYiOy4oeydtCDrqqnsoIHsnbgg7KeA64+E7ZWZ7Iq16rO8IOywqOydtOulvCAg67O07J2064qUIOygkOydtOuLpC4gCgoKIyMgNi4xLjIg642w7J207YSwIOuniOydtOuLneydmCDrjIDtkZzsoIEg6riw64qlCgojIyMg67aE66WY7JmAIOqzhOy4tSDtmZXrpaAg7LaU7KCVCgrrtoTrpZjripQg7Ja064qQIOqzhOy4teyXkCDsho3tlaDsp4Drpbwg7JiI7Lih7ZWY64qUIOqyg+ycvOuhnCjsmIgsIOyVhOuLiOyYpCkg7J206rOgIOyDiOuhreqyjCDrgpjtg4Drgpwg7ZiE7IOB7J2EIOqygO2GoO2VmOyXrCDquLDsobTsnZgg67aE66WYIOygleydmOuQnCDsp5Htlansl5Ag67Cw7KCV7ZWY64qUIOqyg+ydhCDsnZjrr7jtlZzri6QuCgrrjbDsnbTthLAg66eI7J2064udIO2UhOuhnOyEuOyKpOuKlCDqs6DqsJ3snbQg7Ja064qQIOqzhOy4teyXkCDsho3tlaDsp4Ag6rKw7KCV7ZWY64qUIOuqqOuNuOydhCDsg53shLHtlZjripTrjbAg7J20IOqzvOygleydhCDsoJDsiJjtmZQoc2NvcmluZykg65iQ64qUIOqzhOy4tSDtmZXrpaAg7LaU7KCVKGNsYXNzIHByb2JhYmlsaXR5IGVzdGltYXRpb24pIOydtOudvOqzoCDrtoDrpbjri6QuIAoKCiMjIyDsnKDsgqzrj4Qg66ek7LmtCgrsnKDsgqzrj4Qg66ek7LmtKHNpbWlsYXJpdHkgbWF0Y2hpbmcp7J2AIOqzoOqwneyXkOqyjCDsoJztkojsnYQg7LaU7LKY7ZWgIOuVjCDsgqzsmqntlZjripQg6rCA7J6lIOunjuydtCDsgqzsmqntlZjripQg67Cp67KV7KSRIO2VmOuCmOuhnCwg6rWs66ek7ZWY7JiA6rGw64KYIOyEoO2YuO2VmOuKlCDsoJztkojsnZgg6rSA7KCQ7JeQ7IScIO2YhOyerCDqs6DqsJ3qs7wg7Jyg7IKs7ZWcIOyCrOuejOydhCDssL7slYTrgrTripQg6rKD7J2064ukLgoKIyMjIOq1sOynke2ZlCAKCuq1sOynke2ZlChjbHVzdGVyaW5nKSDripQg642w7J207YSw7J2YIOq4sOy0iCDsobDsgqwgKO2DkOyDieyggSDrjbDsnbTthLAg67aE7ISdIEVEQSnrpbwg7IiY7ZaJ7ZWgIOuVjCDslrTrlqQg6re466O57J20IOyekOyXsOyKpOufveqyjCDrp4zrk6TslrQg7KeA64qU7KeA66W8IOyVjOugpOyjvOqzoCwg6rWw7KeR7J20IOyhtOyerO2VmOuptCDri6Trpbgg642w7J207YSwIOuniOydtOuLnSDsnpHsl4XsnYQg7IiY7ZaJ7ZW067O8IO2VhOyalOqwgCDsnojri6TripQg7KCQ7J2EIOyVjCDsiJgg7J6I6riwIOuVjOusuOyXkCDrp6TsmrAg7Jyg7Jqp7ZWcIOuwqeuyleydtOuLpC4g6rWw7KeR7ZmU64qUIOuvuOumrCDsoJXsnZjrkJwg6riw7KSA7J2064KYIOyYiOyLnOyXkCDsnZjsobTtlZjsp4Ag7JWK6rOgIO2KueyglSDrqqnsoIHsnbQg7JeG64qUIOyDge2DnOyXkOyEnCDsnKDsgqzrj4Tsl5Ag7J2Y7ZW0IOq3uOujue2ZlCDrkJzri6QuIOyjvOuhnCDrjbDsnbTthLAg66eI7J2064ud7J2064KYIOuqqOuNuOungSDspIDruYQg64uo6rOE7JeQ7IScIOyjvOuhnCDsgqzsmqnrkJzri6QuIAoKCiMjIyDtmozqt4DrtoTshJ0gCgrslrTrlqQg7J287J20IOyWvOuniOuCmCDrp47snbQg7J287Ja064KY64qU7KeA66W8IOyYiOy4oe2VmOuKlCDqsoPsnbTri6QuIOyYiOuhnCAn7J20IOqzoOqwneydtCDsnbTrj5nsoITtmZQg7ISc67mE7Iqk66W8IOyWvOuniOuCmCDrp47snbQg7IKs7Jqp7ZWg7KeAJyDsmYAg6rCZ7J2AIOyniOusuOydtOuLpC4g7Jes6riw7IScIOyYiOy4oe2VoCDrs4DsiJjripQg7ISc67mE7IqkIOyCrOyaqeufieydtOupsCDri6Trpbgg7Jyg7IKs7ZWcIOqzoOqwneqzvCDqt7jrk6TsnZgg7IKs7Jqp65+JIOydtOugpSDrjbDsnbTthLDrpbwg7IK07Y6067O06rOgIOuqqOuNuOydhCDrp4zrk6TsiJgg7J6I64ukLiDspoksIO2ajOq3gOu2hOyEneydgCDslrTrlqQg6rOg6rCd7JeQIOuMgO2VnCDtirnsoJUg67OA7IiY7J2YIOqwkuydhCDstpTsoJXtlZjripQg66qo64247J2EIOunjOuTnOuKlCDqsoPsnbTri6QuCgoKIyMjIOuPmeyLnOuwnOyDnSDqt7jro7ntmZQgKGNvLW9jY3VycmVuY2UgZ3JvdXBpbmcpIAoK64+Z7Iuc67Cc7IOdIOq3uOujue2ZlCDripQg67mI67Cc7ZWt66qpIOynke2VqSDrp4jsnbTri50gKGZyZXF1ZW50IGl0ZW1zZXQgbWluaW5nKSwg7Jew6rSA7ISx6rec7LmZIOuwnOqyrChBc3NvY2lhdGlvbiBydWxlIGRpc2NvdmVyeSksIOyepeuwlOq1rOuLiOu2hOyEnShtYXJrZXQtYmFza2V0IGFuYWx5c2lzKSDsnbTrnbzqs6Drj4Qg7ZWc64ukLiDsnbTripQg6rCc7LK07JeQIOq0gOugqOuQnCDqsbDrnpjsl5Ag6riw67CY7ZWY7JesIOqwnOyytCDqsITsnZgg7Jew6rSA7ISx7J2EIOywvuyVhOuCuOuLpC4g7JiI66GcICfsnbzrsJjsoIHsnLzroZwg7Ja065akIOyDge2SiOydhCDtlajqu5gg6rWs66ek7ZWY64qU6rCAJyDsmYAg6rCZ7J2AIOyniOusuOydhCDrk6TsiJjsnojri6QuCgoKIyMjIOuNsOydtO2EsCDstpXshowoZGF0YSByZWR1Y3Rpb24pIAoK642w7J207YSwIOy2leyGjOuKlCDrp47snYAg67OA7IiY7J2YIOuNsOydtO2EsCDspokg6rOg7LCo7JuQ7J2YIOuNsOydtO2EsOyXkOyEnCDspJHsmpQg7KCV67O066W8IOyDgeuLueyImCDri7Tqs6Ag7J6I64qUIOuNlCDsoIHsnYAg67OA7IiY7J2YIOuNsOydtO2EsCjsoIDssKjsm5Ag642w7J207YSwKSDshYvsnLzroZwg67CU6r6464qUIOqyg+ydtOuLpC4K7KCB7J2AIOuzgOyImOydmCDrjbDsnbTthLDshYvsnYAg7LKY66as7ZWY6riwIOuNlCDsib3qs6Ag7KCV67O066W8IOywvuyVhOuCtOq4sOuPhCDsiazsm4zsp4Tri6QuIOydvOuwmOyggeycvOuhnCDrjbDsnbTthLAg7LaV7IaM66W8IO2VmOuptCDsoJXrs7TripQg7IaQ7Iuk65CY7KeA66eMIOuNsOydtO2EsOyXkCDrjIDtlZwg7Ya17LCw66Cl7J2EIOyWu+ydhOyImCDsnojripQg7J6l7KCQ7J20IOyeiOuLpC4gCgoKIyMgNi4xLjMg642w7J207YSwIOuniOydtOuLnSDstpTsp4Qg64uo6rOECgojIyMgMSDri6jqs4Q6IOuqqeyggSDsoJXsnZgKCuuNsOydtO2EsCDrp4jsnbTri53snYQg7Ya17ZW0IOyWu+qzoOyekCDtlZjripQg6rKD7J20IOustOyXh+yduOyngCDrqoXtmZXtlZwg66qp7KCB7J2EIOygleydmO2VmOuKlCDri6jqs4TsnbTri6QuIDEg64uo6rOE67aA7YSwIOyghOusuOqwgOqwgCDssLjsl6ztlZjsl6wg66qp7KCB7JeQIOuUsOudvCDsgqzsmqntlaAg642w7J207YSwIOuniOydtOuLnSDrqqjrjbjqs7wg7ZWE7JqU7ZWcIOuNsOydtO2EsOulvCDsoJXsnZjtlZzri6QuCgoKIyMjIDIg64uo6rOEOiDrjbDsnbTthLAg7KSA67mECgrqs6DqsJ3soJXrs7QsIOqxsOuemOygleuztCwg7IOB7ZKI66eI7Iqk7YSwLCDsm7nroZzqt7jrjbDsnbTthLAsIOyCrO2ajOyXsOqysOunnSDrjbDsnbTthLAg65OxIOuLpOyWke2VnCDrjbDsnbTthLDrpbwg7Zmc7Jqp7ZWgIOyImCDsnojrj4TroZ0g7IiY7KeR7ZWY64qUIOuLqOqzhOuLpC4gCklUIOu2gOyEnOyZgCDsgqzsoITtmJHsnZjrpbwg7Ya17ZW0IOuNsOydtO2EsCDsoJHqt7wg67aA7ZWY7JeQIOusuOygnOqwgCDsl4brj4TroZ0g7KGw7JyoLiDrjbDsnbTthLAg7KCV7KCc66W8IO2Gte2VtCDrjbDsnbTthLDsnZgg7ZKI7KeI7J2EIOuztOyepe2VmOqzoCDrjbDsnbTthLAg66eI7J2064udIOq4sOuylSDsoIHsmqnsl5Ag66y47KCc6rCAIOyXhuuPhOuhnSDrjbDsnbTthLAg7JaR7J2EIOy2qeu2hO2eiCDtmZXrs7TtlZzri6QuCgoKIyMjIDMg64uo6rOEOiDrjbDsnbTthLAg6rCA6rO1IO2YueydgCDsoITsspjrpqwKCuuNsOydtO2EsCDrp4jsnbTri50g6riw67KVIOyggeyaqeydtCDqsIDriqXtlZjrj4TroZ0g642w7J207YSw66W8IOqwgOqzte2VmOuKlCDri6jqs4QuIOuqqOuNuOungSDrqqnsoIHsl5Ag65Sw6528IOuqqeyggSDrs4DsiJjrpbwg7KCV7J2Y7ZWY6rOgIO2VhOyalO2VnCDrjbDsnbTthLDrpbwg642w7J207YSwIOuniOydtOuLnSDshoztlITtirjsm6jslrTsl5Ag7KCB7Jqp7ZWg7IiYIOyeiOuPhOuhnSDsoIHtlantlZwg7ZiV7Iud7Jy866GcIOqwgOqzte2VnOuLpC4gCgojIyMgNCDri6jqs4Q6IOuNsOydtO2EsCDrp4jsnbTri50g6riw67KVIOyggeyaqSAKCuykgOu5hO2VnCDrjbDsnbTthLDsl5Ag642w7J207YSwIOuniOydtOuLnSDquLDrspXsnYQg7KCB7Jqp7ZWY64qUIOuLqOqzhOuhnCAg642w7J207YSwIOuniOydtOuLnSDshoztlITtirjsm6jslrTrpbwg7Zmc7Jqp7ZWY7JesIOuqqeygge2VmOuKlCDsoJXrs7Trpbwg7LaU7Lac7ZWc64ukLiAKCiMjIyA1IOuLqOqzhDog642w7J207YSwIOuniOydtOuLnSDsoIHsmqkg6rKw6rO8IOqygOymnSDrsI8g7ZmV7IKwCgrrjbDsnbTthLAg66eI7J2064ud7Jy866GcIOy2lOy2nO2VnCDsoJXrs7Trpbwg6rKA7Kad7ZWY64qUIOuLqOqzhOuhnCDthYzsiqTtirgg66eI7LyA7YyF7J2064KYIOqzvOqxsCDrjbDsnbTthLDrpbwg7Zmc7Jqp7ZW0IO2FjOyKpO2KuOulvCDsiJjtlontlaAg7IiY64+EIOyeiOuLpC4g6rKA7Kad7J20IOyZhOujjOuQmOuptCBJVCDrtoDshJzsmYAg7ZiR7J2Y7ZWY7JesIOyDgeyLnOyggeycvOuhnCDrjbDsnbTthLAg66eI7J2064udIOqysOqzvOulvCDsl4XrrLTsl5Ag7KCB7Jqp7ZWg7IiYIOyeiOuPhOuhnSDsnpDrj5ntmZQg67Cp7JWI7J2EIO2YkeydmO2VnOuLpC4g67O06rOg7ISc66W8IOyekeyEse2VmOyXrCDqsr3smIHsp4Qg67CPIOq1rOyEseybkOyXkOqyjCDsl7DqsIQg7LaU6rCAIOyImOydteqzvCDtiKzsnpDrjIDruYTshLEoUk9JKeuTseycvOuhnCDquLDrjIAg7Zqo6rO866W8IOyghO2MjO2VnOuLpC4gCgoKIyMgNi4xLjQg67aE66WY67aE7ISd7J2YIOyjvOyalCDrqqjrjbgKCuydmOyCrOqysOygleuCmOustDog66qp7ZGc67OA7IiY7JmAIOqwgOyepSDsl7DqtIDshLHsnbQg64aS7J2AIOuzgOyImOydmCDsiJzshJzrjIDroZwg7KeA64uI7KeA7IiY64KYIOyXlO2KuOuhnO2UvCDrk7HsnbQg64Ku7JWE7KeA64qUIOuwqe2WpeycvOuhnCDtirjrpqwg7ZiV7YOc66GcIOqwgOyngOulvCDrtoTtlaDtlZjrqbTshJwg67aE66WYIOq4sOuyleydhCDrp4zrk6TslrTrgrTripQg6riw67KVLiDsnbTqsoPsnYQg67aE7ZWgIOygleuzteq4sOuylSDsnbTrnbzqs6Drj4Qg7ZWc64ukCgrroZzsp4DsiqTti7Eg7ZqM6reAOiDshKTrqoXrs4DsiJjqsJLsnbQg7KO87Ja07KGM7J2EIOuVjCwg66qp7ZGc67OA7IiY6rCS7J20IO2KueyglSDrtoDrpZjsl5Ag7IaN7ZWgIO2ZleuloOydtCDroZzsp4DsiqTti7Eg7ZWo7IiYIO2Yle2DnOulvCDrlLDrpbjri6Tqs6Ag6rCA7KCV7ZW0IOy1nOuMgCDsmrDrj4Qg7LaU7KCVIOuwqeuylShtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbikg7J2E66GcIOuqqe2RnOuzgOyImOydmCDtmZXrpaDsnYQg7LaU7KCV7ZWY64qUIOq4sOuylS4KCuyduOqzteyLoOqyveunnTog7J246rCEIOuHjOydmCDribTrn7DsnpHsmqkg7ZiV7YOc7JeQ7IScIOuqqO2LsOu4jOulvCDslrvsnYQg6riw67KV7Jy866GcIOyeheugpSwg7J2A64uJLCDstpzroKUg64W465Oc66GcIOq1rOyEse2VmOyXrCDrs7XsnqHtlZwg67aE66WY64KYIOyImOy5mCDsmIjsuKEg66y47KCc66W8IO2VtOqysO2VoCDsiJgg7J6I64+E66GdIO2VmOuKlCDrtoTshJ0g6riw67KVLgoK656c642kIO2PrOugiOyKpO2KuDog7KO87Ja07KeEIOuNsOydtO2EsOuhnOu2gO2EsCDsl6zrn6wg6rCc7J2YIOuLpOyWke2VnCDsnZjsgqzqsrDsoJUg7Yq466as66W8IOunjOuTpOyWtCDqsIEg7J2Y7IKs6rKw7KCVIO2KuOumrOydmCDsmIjsuKHqsrDqs7zrpbwg7Yis7ZGc7ZiV7Iud7Jy866GcIOynkeqzhO2VmOyXrCDstZzsooUg67aE66WYIOqysOqzvOulvCDqsrDsoJXtlZjripQg7JWZ7IOB67iUIO2Yle2DnOydmCDquLDrspUuCgrshJztj6ztirgg67Kh7YSwIOuouOyLoDog7Yq57KCVIOuNsOydtO2EsOulvCDrtoTrpZjtlZjripTrjbAg7J6I7Ja0IOyEnOuhnCDri6Trpbgg67aE66WY7JeQIOyGje2VnCDrjbDsnbTthLAg6rCE7J2YIOqwhOqyqeydtCDstZzrjIDtmZTqsIAg65CY64qUIO2PieuptOydhCDssL7slYQg7J2066W8IOq4sOykgOycvOuhnCDrtoTrpZjtlZjripQg6riw67KVLgoK64KY7J2067iMIOuyoOydtOymiDog67Kg7J207KaIIOygleumrOyXkCDqt7zqsbDtlZjsl6wg66qp7ZGc67OA7IiY6rCAIOuwnOyDne2VoCDsobDqsbTrtoAg7ZmV66Wg7J2EIOyCrOyghCDtmZXrpaDqs7wg7Jqw64+EIO2VqOyImOydmCDqs7HsnLzroZwg7ZGc7ZiE7ZWY7JesIOyWtOuWpCDrtoTrpZgg7ZWt66qp7JeQIOyGje2VoOyngOulvCDqs4TsgrDtlZjsl6wg7ZmV66Wg7J20IOuGkuydgCDsqr3snLzroZwg67aE66WY7ZWY64qUIOq4sOuylS4KCkst7LWc6re87KCRIOydtOybgzog7Yq57KCVIOuNsOydtO2EsCDsooztkZzsoJDqs7wg64uk66W4IOuCmOuouOyngCDrjbDsnbTthLAg7KKM7ZGc7KCQIOqwhOydmCDqsbDrpqzsl5Ag6riw67CY7J2EIOuRkOyWtCDqsIDsnqUg6rCA6rmM7Jq0IGvqsJwg7KCQ65Ok7J2YIOuqqe2RnOuzgOyImOqwkuydhCDri6TsiJjqsrDroZwg67aE66WY7ZWY64qUIOq4sOuylS4g6rKM7Jy866W4IO2VmeyKteydtOudvOqzoOuPhCDtlZzri6QuCgoKCiMjIDYuMiDsnZjsgqzqsrDsoJXrgpjrrLQgCgrsnZjsgqzqsrDsoJUg64KY66y0IOuqqOuNuOydgCDsnZjsgqzqsrDsoJUg6rec7LmZ7J2EIOuCmOustCDqtazsobDsl5Ag7J2Y7ZWcIOy2lOuhoCDqt5zsuZnsnLzroZwg7ZGc7ZiE7ZWY7JesIOyghOyytCDsnpDro4zrpbwg66qHIOqwnOydmCDshozsp5Hri6jsnLzroZwg67aE66WY7ZWY6rGw64KYIOyYiOy4oeydhCDsiJjtlontlZjripQg67aE7ISdIOuwqeuyleydtOuLpC4KCuuqqe2RnOuzgOyImOqwgCDsnbTsgrDtmJXsnbgg6rK97JqwIOu2hOulmOuCmOustOudvOqzoCDtlZjqs6AsIOyXsOyGje2YleyduCDqsr3smrAg7ZqM6reA64KY66y065286rOgIO2VnOuLpC4KCuuqqe2RnOuzgOyImOqwgCDsnbTsgrDtmJXsnbgg67aE66WY64KY66y07J2YIOqyveyasCDsg4HsnIQg64W465Oc7JeQ7IScIOqwgOyngCDrtoTtlaDsnYQg7IiY7ZaJ7ZWgIOuVjCDrtoTrpZgg67OA7IiY7JmAIOu2hOulmCDquLDspIDqsJLsnZgg7ISg7YOdIOuwqeuyleycvOuhnCDsubTsnbTsoJzqs7Eg7Ya16rOE65+J7J2YIHAtdmFsdWUsIOyngOuLiOyngOyImCDsl5TtirjroZztlLzsp4DsiJgg65Ox7J20IOyCrOyaqeuQnOuLpC4gCgrsnZjsgqzqsrDsoJXrgpjrrLQg66qo64247J2AIOyLnOyepeyhsOyCrCwg6rSR6rOg7KGw7IKsLCDsnZjtlZnsl7DqtawsIO2SiOyniOq0gOumrCDrk7HsnZgg64uk7JaR7ZWcIOu2hOyVvOyXkOyEnCDtmZzsmqnrkJjqs6Ag7J6I64ukLgoKY3JlYXRlRGF0YVBhcnRpdGlvbigp7ZWo7IiY64qUIOu2hOulmOulvCDquLDspIDsnLzroZwg7ZuI66Co7JqpIOuNsOydtO2EsOyXkOyEnCDsgqzsmqntlaAg642w7J207YSw66W8IOyEpOygle2VnCDruYTsnKjroZwg67aE66as7ZWc64ukLgoKcHJlZGljdCgpIO2VqOyImOuKlCDthYzsiqTtirgg642w7J207YSw66GcIOydmOyCrOqysOygleuCmOustCDrqqjrjbjsnYQg7IKs7Jqp7ZWcIOyYiOy4oeydhCDsiJjtlontlZzri6QuCgpycGFydC5wbG90IO2MqO2CpOyngOydmCBwcnAoKe2VqOyImOuhnCDsoIHtlanrkJwg7J2Y7IKs6rKw7KCV64KY66y0IOuqqOuNuOydhCDsi5zqsIHtmZTtlZzri6QuCgoKIyMgNi4yLjEg7J2Y7IKs6rK97KCV64KY66y0IOuqqOuNuOydmCDqsJzrhZAKCuydmOyCrOqysOygleuCmOustCDrqqjrjbjsnYAg7J2Y7IKs6rKo7KCVIOq3nOy5meydhCDrgpjrrLTqtazsobDsl5Ag7J2Y7ZWcIOy2lOuhoCDqt5zsuZnsnLzroZwg7ZGc7ZiE7ZWY7JesIOyghOyytCDsnpDro4zrpbwg66qHIOqwnOydmCDshozsp5Hri6jsnLzroZwg67aE66WY7ZWY6rGw64KYIOyYiOy4oeydhCDsiJjtlontlZjripQg67aE7ISdIOuwqeuyleydtOuLpC4KCgojIyA2LjIuMiDrtoTrpZgg67OA7IiY7JmAIOu2hOulmCDquLDspIDqsJLsnZgg7ISg7YOdIOuwqeuylQoK67aE66WYIOuzgOyImOyZgCDrtoTrpZgg6riw7KSA6rCS7J2YIOyEoO2DnSDrsKnrspXsnLzroZwg7Lm07J207KCc6rOxIO2GteqzhOufieydmCBwLXZhbHVlLCDsp4Dri4jsp4DsiJgsIOyXlO2KuOuhnO2UvOyngOyImCDrk7HsnbQg7IKs7Jqp65Cc64ukLiDshKDtg50g6riw7KSA7JeQIOydmO2VnCDrtoTtlaDsnbQg7J287Ja064KgIOuVjCDsubTsnbTsoJzqs7Eg7Ya16rOE65607J2YIHAtdmFsdWXripQg6re46rCS7J20IOyekeydhOyImOuhnSDsnpDsi53rhbjrk5wg64K07J2YIOu2iO2ZleyLpOyEseydtCDtgbzsnYQg64KY7YOA64K066mwLCDsp4Dri4jsp4DsiJjrgpgg7JeU7Yq466Gc7ZS87KeA7IiYIOyXreyLnCDqt7jqsJLsnbQg7YG07IiY66GdIOyekOyLneuFuOuTnCDrgrTsnZgg7J207KeI7ISx7J20IO2BvOydhCDsnZjrr7jtlZzri6QuIOuUsOudvOyEnCDsnbQg6rCS65Ok7J20IOqwgOyepSDsnpHslYTsp4DripQg67Cp7Zal7Jy866GcIOqwgOyngCDrtoTtlaDsnYQg7IiY7ZaJ7ZWY6rKMIOuQnOuLpC4KCuuNsOydtO2EsOqwgCDslrzrp4jrgpgg7J6YIOu2hOumrOuQmOyXiOuKlOyngOulvCDtj4nqsIDtlZjquLAg7JyE7ZW07ISc64qUIOydvOuwmOyggeycvOuhnCDsp4Dri4jsp4DsiJjrpbwg7IKs7Jqp7ZWY66mwIOydtOufrO2VnCDsp4Dri4jsp4DsiJjripQg64W465Oc7JeQIOyXrOufrCDrtoTrpZjqsIAg7ISe7JesIOyeiOydhOyImOuhnSDrhpLqs6AsIOuFuOuTnOyXkCDtlZjrgpjsnZgg67aE66WY66eMIOyhtOyerO2VoCDrlYwg6rCA7J6lIOuCruyVhOynhOuLpC4g7KaJLCDrhbjrk5wg67aE66asIO2bhCDqsIEg64W465Oc7J2YIOu2iO2ZleyLpOyEsSDsuKHsoJXsp4DsiJjqsIAg64Ku7JWE7KeI7IiY66GdIO2KuOumrOu2hOulmOqwgCDsnpgg65CcIOqyg+ycvOuhnCDrs7wg7IiYIOyeiOuLpC4KCiMjIDYuMi4zIOydmOyCrOqysOygleuCmOustOydmCDqtazsobAKIAog66qp7ZGc67OA7IiY6rCAIOyXsOyGje2YleyduCDtmozqt4DrgpjrrLTsnZgg6rK97JqwIOu2hOulmCDrs4DsiJjsmYAg67aE66WYIOq4sOykgOqwkuydmCDshKDtg50g67Cp67KV7Jy866GcIEYt7Ya16rOE65+J7J2AIOq3uCDqsJLsnbQg7YG07IiY66GdIOyYpOywqOydmCDrs4Drj5nsl5Ag67mE7ZW0IOyymOumrOydmCDrs4Drj5nsnbQg7YGs64uk64qUIOqyg+ydhCDsnZjrr7jtlZjrqbAsIOydtOuKlCDsnpDsi53rp4jrlJQg7IKs7J206rCAIOydtOyniOyggeydtOuLpOuKlCDqsoPsnYQg7J2Y66+47ZWY66+A66GcIOydtCDqsJLsnbQg7Luk7KeA64qUIChwLXZhbHVlIOyekeyVhOyngOuKlCkg67Cp7Zal7Jy866GcIOqwgOyngCDrtoTtlaDsnYQg7IiY7ZaJ7ZWY6rKMIOuQnOuLpC4gCuu2hOyCsOydmCDqsJDshozrn4nrj4Qg7J20IOqwkuydtCDstZzrjIDtmZTrkJjripQg67Cp7Zal7Jy866GcIOqwgOyngCDrtoTtlaDsnYQg7IiY7ZaJ7ZWY6rKMIOuQnOuLpC4gCgrrv4zrpqzrp4jrlJQ6IOunqCDsnITsnZgg66eI65SU66W8IOu/jOumrOuniOuUlChyb290IG5vZGUpCgrrtoDrqqjrp4jrlJQ6IOyDgeychOuniOuUlOqwgCDtlZjsnITrp4jrlJTroZwg67aE6riw65CgIOuVjCDsg4HsnITrp4jrlJQgCgrqsIDsp4DrtoTtlaA6IOuCmOustOydmCDqsIDsp4Drpbwg7IOd7ISx7ZWY64qUIOqzvOygleydtOuLpCAKCuqwgOyngOy5mOq4sDog7IOd7ISx65CcIOqwgOyngOulvCDsnpjrnbzrgrTslrQg66qo64247J2EIOuLqOyInO2ZlO2VmOuKlCDqs7zsoJXsnYQg66eQ7ZWc64ukLgoK6rmK7J20OiDrv4zrpqzrp4jrlJTrtoDthLAg7LWc7KKF66eI65SU6rmM7KeA7J2YIOykkeqwhOuniOuUlOuTpOydmCDsiJgKCioq7J2Y7IKs6rKw7KCV64KY66y0IOyepSwg64uo7KCQKiogCgoq7J6l7KCQOioKCuq1rOyhsOqwgCDri6jsiJztlbTshJwg7ZW07ISd7J20IOyaqeydtO2VmOuLpAoK67aE66WYLCDsiJjsuZjsmIjsuKEg66qo65GQIO2ZnOyaqeqwgOuKpQoK7ISg7ZiV7ISxLCDsoJXqt5zshLEsIOuTseu2hOyCsOyEsSDrk7HsnZgg7IiY7ZWZ7KCBIOqwgOygleydtCDrtojtlYTsmpTtlZjri6QuIAoK642w7J207YSw7JeQIOqysOy4oeqwkuydtCDsnojripQg6rK97Jqw7JeQ64+EIO2aqOqzvOyggeycvOuhnCDsspjrpqzqsIDriqUgCgrspJHsmpTtlZwg67OA7IiY66eMIOyEoOuzhO2VoCDsiJjsnojqs6AsIOydtOulvCDthrXtlbQg64uk66W4IOy2lOqwgCDrtoTshJ3snYQg7JyE7ZWcIO2GteywsOugpeydhCDslrvsnYQg7IiYIOyeiOuLpC4gCgrrtoTrpZgg6rKw6rO87JeQIOuMgO2VnCDqt5zsuZnquLDrsJjsnZgg7ZW07ISd7J20IOqwgOuKpe2VmOyXrCDqsrDqs7wg7ZW07ISd7JeQIOycoOyaqe2VmOuLpC4gCgoKKuuLqOygkDoqCgrsl7Dsho3tmJUg7J6F66Cl67OA7IiY66W8IOu5hOyXsOyGjeyggeyduCDqsJLsnLzroZwg7Leo6riJ7ZWY66+A66GcLCDrtoTrpqzsnZgg6rK96rOE7KCQIOq3vOuwqeyXkOyEnCDsmIjsuKEg7Jik66WYIOqwgOuKpeyEseydtCDsnojri6QuCgrshKDtmJUg65iQ64qUIOyjvO2aqOqzvCDrqqjrjbjqs7wg6rCZ7J2AIO2VtOyEneydtCDrtojqsIDriqXtlajsnLzroZwg66qo64247Iud7J2EIOyImOumve2VtOyVvCDtlZjripQg6rK97JqwIOyggeyaqeydtCDslrTroLXri6QuCgrtm4jroKjsmqkg642w7J207YSw7JeQIOuMgO2VnCDslb3qsITsnZgg67OA6rK9IOuwnOyDnSDsi5wg7Yq466asIOu2hOulmCDqsrDsoJUg64W866as7JeQIO2BsCDrs4DtmZTrpbwg6rCA7KC47Jio64ukIAoK66qo64247J20IOyJveqyjCDqs7zsoIHtlanrkJjqsbDrgpgg6rO87IaM7KCB7ZWpIOuQoCDsiJgg7J6I64ukLgoK7Yq466as6rCAIOuEiOustCDsu6Tsp4gg6rK97JqwIO2MqO2EtOydhCDsnbTtlbTtlZjquLDqsIAg7Im97KeAIOyViuuLpC4gCgoK7J2Y7IKs6rKw7KCV64KY66y0IOuqqOuNuOydhCDsnITtlZwg7JWM6rOg66as7KaY7JeQ64qUIENIQUlEKGNoaS1zcXVhcmVkIGF1dG9tYXRpYyBpbnRlcmFjdGlvbiBkZXRlY3Rpb24pLCBDQVJUKGNsYXNzaWZpY2F0aW9uIGFuZCByZWdyZXNzaW9uIHRyZWUpLCBJRDMsIEM0LjUsIEM1LjAgIOuTseydtCDsnojri6QuIAoKCuqzvOygge2VqTog7ZuI66Co7JqpIOuNsOydtO2EsOyXkCDrjIDtlbTshJzripQg64aS7J2AIOygle2ZleuPhOulvCDrgpjtg4DrgrTsp4Drp4wg7IOI66Gc7Jq0IOuNsOydtO2EsOyXkCDrjIDtlbTshJzripQg7JiI7Lih7J2EIOyemO2VmOyngCDrqrvtlZjripQg6rKD7J2EIOunkO2VnOuLpC4gCgrqs7zshozsoIHtlak6IOuNsOydtO2EsOulvCDstqnrtoTtnogg67CY7JiB7ZWY7KeAIOuqu+2VtCjsmIjroZwg7IOY7ZSMIOyImOqwgCDstqnrtoTtlZjsp4Ag7JWK7J2AIOqyveyasCkg7J6h7J2M7J20IOunjuydtCDshJ7sl6wg7J6I7Ja0IOuCruydgCDshLHqs7zrpbwg67O07J2064qUIOusuOygnC4KCgoKIyMgNi4yLjQg7J2Y7IKs6rKw7KCV64KY66y0IOu2hOyEnSDsmIjsoJwgKHJwYXJ0IO2VqOyImCkKCuuLpOydjOydgCBycGFydCDtjKjtgqTsp4DsnZggcnBhcnQoKSDtlajsiJjroZwg7JWE7J2066as7IqkIOuNsOydtO2EsOyFi+ydhCDsnbTsmqntlZwg7J2Y7IKs6rKw7KCV64KY66y0IOu2hOyEneydhCDsiJjtlontlZjripQg7JiI7KCc64ukLiAKCioqMS4g7JWE7J2066as7IqkIOuNsOydtO2EsOyFi+ydhCDtm4jroKjsmqkg642w7J207YSwIOyZgCDthYzsiqTtirgg642w7J207YSw66GcIOu2hOumrO2VmOq4sCoqCgpgYGB7cn0KbGlicmFyeShjYXJldCkKCmhlYWQoaXJpcykKc3VtbWFyeShpcmlzKQpzdHIoaXJpcykKCiMgODDtlITroZwg7ISgIApwYXJ0czwtY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHA9MC44KQpzdW1tYXJ5KHBhcnRzKQoKIyDtirjroIjsnbTri50g642w7J207YSwIApkYXRhLnRyYWluPC1pcmlzW3BhcnRzJFJlc2FtcGxlMSxdCnRhYmxlKGRhdGEudHJhaW4kU3BlY2llcykKCiMg7YWM7Iqk7Yq4IOuNsOydtO2EsCAKZGF0YS50ZXN0PC1pcmlzWy1wYXJ0cyRSZXNhbXBsZTEsXQp0YWJsZShkYXRhLnRlc3QkU3BlY2llcykKYGBgCgoKCioqMi4g7ZuI66Co7JqpIOuNsOydtO2EsOuhnCDsnZjsgqzqsrDsoJXrgpjrrLQg66qo6424IO2VmeyKte2VmOq4sCoqCgpgYGB7cn0KbGlicmFyeShycGFydCkKCiMg7J2Y7IKs6rKw7KCV64KY66y0IOuqqOuNuCDtlZnsirXsnYQg7JyE7ZW0IOyiheyEneuzgOyImOulvCBTcGVjaWVzLCDrj4Xrpr3rs4DsiJjrpbwgU2VwYWwuTGVuZ3RoLCBTZXBhbC5XaWR0aCwgUGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aApkdC5tPC1ycGFydChTcGVjaWVzfi4gLGRhdGE9ZGF0YS50cmFpbikKcHJpbnQoZHQubSkKYGBgCgoKKiozLiDsoIHtlanrkJwg7J2Y7IKs6rKw7KCV64KY66y0IOuqqOuNuCDsi5zqsIHtmZQqKgoKYGBge3J9CnBsb3QoZHQubSwgY29tcHJlc3MgPSBUUlVFLCBtYXJnaW4gPSAwLjMpCnRleHQoZHQubSxjZXggPSAxLjIpCmBgYAoKCgoqKu2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7Lih7J2EIOyImO2WiSwg7J2Y7IKs6rKw7KCV64KY66y0IOuqqOuNuOydmCDshLHriqUg7Y+J6rCA7ZWY6riwKioKCmBgYHtyfQpkdC5tLnByZWQ8LXByZWRpY3QoZHQubSwgbmV3ZGF0YSA9IGRhdGEudGVzdCwgdHlwZSA9ICdjbGFzcycpCgpjb25mdXNpb25NYXRyaXgoZGF0YS50ZXN0JFNwZWNpZXMsIGR0Lm0ucHJlZCkKYGBgCgoK7KCV67aE66WY7JyoIChBY2N1cmFjeSnripQgMTAwIO2UhOuhnOydtOuLpC4gCgoqKjUuIHJwYXJ0LnBsb3Qg66GcIOuqqOuNuCDsi5zqsIHtmZQqKgoKYGBge3J9CmxpYnJhcnkocnBhcnQucGxvdCkKcHJwKGR0Lm0sIGV4dHJhID0gMikKYGBgCgpQZXRhbC5MZW5ndGgg6rCAIDIuNSDrs7Tri6Qg7KCB7J2A6rCc7LK064qUIDQwIOqwnCDsnojripTrjbAg6re47KSRIDQw6rCcIOyghOu2gOqwgCBzZXRvc2Eg7J2064ukLgoKUGV0YWwuTGVuZ3RoIOqwgCAyLjUg67O064ukIO2BrOqzoCBQZXRhbC5XaXRoIOqwgCAxLjgg67O064ukIOyekeydgCDqsJzssrTripQgNDQg6rCc7J24642wIOq3uOykkSAzOSDqsJzqsIAgdmVyc2ljb2wg7J2064ukLiAKClBldGFsLkxlbmd0aCDqsIAgMi41IOuztOuLpCDtgazqs6AgUGV0YWwuV2l0aCDqsIAgMS44IOuztOuLpCDtgbAg6rCc7LK064qUIDM2IOqwnOyduOuNsCDqt7jspJEgMzUg6rCc64qUIHZpcmdpbmljIOydtOuLpC4gCgoKCiMjIDYuMi41IOydmOyCrOqysOygleuCmOustCDrtoTshJ0g7JiI7KCcIChjbmVlIO2VqOyImCkKCioqMS4g7ZuI66Co7JqpIOuNsOydtO2EsOuhnCDsnZjsgqzqsrDsoJXrgpjrrLQg66qo6424IO2VmeyKte2VmOq4sCoqCgpgYGB7cn0KbGlicmFyeShwYXJ0eSkKCmR0Lm0yPC0gY3RyZWUoU3BlY2llcyB+IC4sIGRhdGEgPSBkYXRhLnRyYWluKQpwcmludChkdC5tMikKYGBgCgoKKioyLiDsi5zqsIHtmZQqKgoKYGBge3J9CnBsb3QoZHQubTIpCmBgYAoKCgoqKjMuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7LihLCDsnZjsgqzqsrDsoJXrgpjrrLQg66qo6424IOyEseuKpSDtj4nqsIAqKgoKYGBge3J9CmR0Lm0yLnByZWQ8LXByZWRpY3QoZHQubTIsIG5ld2RhdGEgPSBkYXRhLnRlc3QpCgpjb25mdXNpb25NYXRyaXgoZGF0YS50ZXN0JFNwZWNpZXMsIGR0Lm0yLnByZWQpCmBgYAoK7KCV67aE66WY7JyoKEFjY3VyYWN5KSDripQgMC45NjY3LgoKCiMjIDYuMyDroZzsp4DsiqTti7Eg7ZqM6reAIAoKCuuhnOyngOyKpO2LsSDtmozqt4Ag66qo64247J2AIOuqqe2RnOuzgOyImCAo65iQ64qUIOyiheyGjeuzgOyImCkg6rCAIOuylOyjvO2YleyduCDqsr3smrDsl5Ag7KCB7Jqp65CY64qUIO2ajOq3gOu2hOyEnSDrqqjrjbjsnbTri6QuIAoK7Jik7KaI64qUIOyEseqzte2VoCDtmZXrpaDsnbQg7Iuk7Yyo7ZWgIO2ZleuloOydmCDrqocg67Cw7J247KeA66W8IOuCmO2DgOuCtOuKlCDtmZXrpaDsnbTri6QuIAoKY29tcGxldGUuY2FzZXMoKSDtlajsiJjripQg7ZW064u5IO2WieydmCDrqqjrk6Ag6rCS7J20IAoK66Gc7KeA7Iqk7YuxIO2ajOq3gCDrqqjrjbjsnZgg6rCc64WQIE5BIOqwgCDslYTri4wg6rK97JqwIFRSVUUsIO2VtOuLuSDtlonsnZgg6rCS7J20IO2VmOuCmOudvOuPhCBOQSDrpbwg7Y+s7ZWo7ZWY6rOgIOyeiOuKlCDqsr3smrAgRkFMU0XqsJLsnYQg67CY7ZmY7ZWc64ukLiAKCmR1cGxpY2F0ZWQoKSDtlajsiJjripQg7KSR67O1IOqwkuydtCDsobTsnqztlZjripQg6rK97JqwIFRSVUUsIOyVhOuLjCDqsr3smrAgRkFMU0Xrpbwg7Lac66Cl7ZWc64ukLiAKCnBlcmZvcm1hbmNlQW5hbHl0aWNzIO2MqO2CpOyngOydmCBjaGFydC5Db3JyZWxhdGlvbigp7ZWo7IiY64qUIOyCsOygkOyZgCDsg4HqtIDqs4TsiJjrpbwg7Lac66Cl7ZWc64ukLgoKR0dhbGx5IO2MqO2CpOyngOydmCBnZ2NvcnIoKe2VqOyImOuKlCDshKTrqoXrs4DsiJgg6rCE7J2YIOyDgeq0gOqzhOyImCDtnojtirjrp7XsnYQg7Iuc6rCB7ZmU7ZWc64ukLgoKZm1zYiDtjKjtgqTsp4DsnZggVklGKCntlajsiJjripQg67aE7IKw7Yy97LC97KeA7IiY66W8IOy2nOugpe2VnOuLpC4KCnNjYWxlKCntlajsiJjripQg7ISk66qF67OA7IiY66W8IO2Pieq3oOydtCAwLCDrtoTsgrDsnbQgMeyduCDqsJLsnLzroZwg7ZGc7KSA7ZmU7ZWc64ukLgoKZ2xtKCntlajsiJjripQg66Gc7KeA7Iqk7YuxIO2ajOq3gCDrqqjrjbjsnYQg7IOd7ISx7ZWc64ukLiAKCnN0ZXAoKSDtlajsiJjsnZggZGlyZWN0aW9uID0gJ2JhY2t3YXJkJyDsmLXshZjsnYAg7ZuE7KeE7KCc6rGw67KV7J2EIOyCrOyaqe2VtCDrqqjrjbjsnYQg7KCB7ZWp7ZWc64ukLiAKCmFub3ZhKCntlajsiJjripQg7ZWY64KYIOydtOyDgeydmCDsoIHtlanrkJwg66qo64247JeQIOuMgO2VnCDrtoTsgrDrtoTshJ3snYQg7IiY7ZaJ7ZWc64ukLiDrtoTsgrDrtoTshJ3snYAg66qo64247J2EIO2PieqwgO2VmOqxsOuCmCDrqqjrjbgg6rCE7J2YIOu5hOq1kOulvCDsnITtlbQg7IKs7Jqp65Cc64ukLiAKCgoKIyMgNi4zLjEg66Gc7KeA7Iqk7YuxIO2ajOq3gCDrqqjrjbjsnZgg6rCc64WQIAoK66Gc7KeA7Iqk7YuxIO2ajOq3gOuqqOuNuOydgCDrqqntkZzrs4DsiJjqsIAg67KU7KO87ZiV7J24IOqyveyasOyXkCDsoIHsmqnrkJjripQg7ZqM6reA67aE7ISdIOuqqOuNuOydtOuLpC4gCgrsg4jroZzsmrQg7ISk66qF67OA7IiYIOuYkOuKlCDrj4Xrpr3rs4DsiJjsnZgg6rCS7J20IOyjvOyWtOyniCDrlYwg66qp7ZGc67OA7IiY7J2YIOqwgSDrspTso7wg65iQ64qUIOynkeuLqOyXkCDsho3tlaAg7ZmV66Wg7J20IOyWvOuniOyduOyngOulvCDstpTsoJUv7JiI7LihIO2VmOyXrCDstpTsoJXtmZXrpaDsnZgg6riw7KSA7LmY7JeQIOuUsOudvCDrtoTrpZjtlZjripQg66qp7KCB7Jy866GcIOyCrOyaqeuQoCDsiJgg7J6I64ukLiDsnbTrlYwg66qo64247J2YIOygge2VqeydhCDthrXtlbQg7LaU7KCV65CcIO2ZleuloOydhCDsgqztm4TtmZXrpaAg7J2065286rOgIO2VnOuLpC4gKHBvc3RlcmlvciBwcm9iYWJpbGl0eSkKCgoqKuyEoO2YlSDtmozqt4DsmYAg66Gc7KeA7Iqk7YuxIO2ajOq3gCDruYTqtZAqKiAKCuyEoO2Yle2ajOq3gCDsooXsho3rs4DsiJg6IOyXsOyGje2YlSDrs4DsiJg7IOuhnOyngOyKpO2LsSDsooXsho3rs4DsiJg6IOydtO2VrSDrsJjsnZHrs4DsiJggKDAsMSkKCuyEoO2Yle2ajOq3gCDqs4TsiJjstpTsoJXrspU6IOy1nOyGjOygnOqzseuylTsg66Gc7KeA7Iqk7YuxIOqzhOyImOy2lOygleuylTog7LWc64yA7Jqw64+E7LaU7KCV67KVIAoK7ISg7ZiV7ZqM6reAIOuqqOuNuCDqsoDsoJU6IEYg6rKA7KCVLCBUIOqygOyglTsg66Gc7KeA7Iqk7YuxIOuqqOuNuOqygOyglTog7Lm07J207KCc6rOxIOqygOyglSAKCgoqKuuhnOyngOyKpO2LsSDtmozqt4Ag66qo64247J2YIOyepSwg64uo7KCQIOu5hOq1kCoqCgoq7J6l7KCQOioKCuyEoO2Yle2GteqzhCDrqqjrjbjsnZgg7J2066Gg7JeQIOq4sOuwmO2VnCDsoJXqtZDtlZjqs6Ag7LK06rOE7KCB7J24IOuqqOyImCDstpTsoJXsnbQg6rCA64ql7ZWY64ukLgoK7ZmV66WgIOuqqOuNuOydtOuvgOuhnCDrqqntkZzrs4DsiJjsnZgg67KU7KO8IO2ZleuloOqwkuydhCDstpTsoJXtlaDsiJgg7J6I64ukLgoK7LaU7KCV65CcIOuqqOuNuOydmCDqs4TsiJjsl5Ag64yA7ZWcIO2VtOyEneydtCDqsIDriqXtlZjrqbAsIOuPheumveuzgOyImOuTpOydmCDsnKDsnZjshLEg67CPIOyYge2WpeugpSDrk7Eg6rKw6rO8IOu2hOyEnSDsi5wg7Jyg7Jqp7ZWcIO2VtOyEneydtCDqsIDriqXtlZjri6QuIAoKCirri6jsoJA6KgoK642w7J207YSw7IWL7J2YIOywqOybkOydtCDrp6TsmrAg66eO7J2EIOuVjCDrqqjrjbjsnZgg7LaU7KCVIOygle2ZleuPhOqwgCDri6Trpbgg67aE66WYIOq4sOuyleyXkCDruYTtlbQg7KKL7KeAIOyViuuLpC4gCgrrs7XsnqHtlZwg67mE7ISg7ZiV7KCBIOu2hOulmOqwgCDtlYTsmpTtlZwg6rK97Jqw7JeQ64qUIOu2hOulmCDsoJXtmZXrj4TqsIAg7KKL7KeAIOyViuuLpC4gCgrstpTsoJUg67Cp67KV7IOBIHgg6rCS7J20IOunpOyasCDsu6Tsp4DqsbDrgpgg7J6R7JWE7KeA66m0IO2ZleuloOqwkuydtCAxIO2YueydgCAwIOyXkCDrp6TsmrAg6rCA6rmM7JuM7KC47IScIOyImOy5mOqzhOyCsCDsoJXtmZXrj4TqsIAg65ao7Ja07KeA6rKMIOuQmOupsCwg67CY67O1IOqzhOyCsCDsi5wg6rO87KCB7ZWp7J20IOu5iOuyiO2VmOqyjCDrsJzsg53tlZzri6QuIAoKCgojIyA2LjMuMiDroZzsp4DsiqTti7Eg7ZqM6reAIOuqqOuNuCDsmIjsoJwoZ2xtIO2VqOyImCkKCgrri6TsnYzsnYAgZ2xtIO2VqOyImOulvCDsnbTsmqntlbQgbWxiZW5jaCDtjKjtgqTsp4DsnZgg7Jyg67Cp7JWUIOuNsOydtO2EsOyFi+ycvOuhnCDroZzsp4DsiqTti7Eg7ZqM6reA67aE7ISd7J2EIOyImO2Wie2VmOuKlCDsmIjsoJzri6QuIArsnbTripQg642w7J207YSwIOyFiyDshozqsJwsIO2DkOyDieyggSDrjbDsnbTthLAg67aE7ISdLCDrs4DsiJgg7ISg7YOdLCDrqqntkZzrs4DsiJjsmYAg7ISk66qF67OA7IiYIOqwhOydmCDsg4HqtIDrtoTshJ0sIOuqqOuNuCDtj4nqsIAg7Iic7Jy866GcIOynhO2Wie2VnOuLpC4gCgoqKjEuIOycoOuwqeyVlCDrjbDsnbTthLDshYsg67aI65+s7Jik6riwKioKCmBgYHtyfQpsaWJyYXJ5KG1sYmVuY2gpCmRhdGEoJ0JyZWFzdENhbmNlcicpCnN0cihCcmVhc3RDYW5jZXIpCnRhYmxlKEJyZWFzdENhbmNlciRDbGFzcykKYGBgCuuwmOydkeuzgOyImCBDbGFzcyDripQg7JaR7ISxIGJlZ2luIOqzvCDslYXshLEgbWFsaWduYW50IOuRkCDqsIDsp4Ag67KU7KO866GcIOu2hOulmOuQnOuLpC4gCgoKCioqMi4g6rKw7Lih6rCSIO2ZleyduCDrsI8g7KCc6rGw7ZWY6riwKioKCmBgYHtyfQpjb2xTdW1zKGlzLm5hKEJyZWFzdENhbmNlcikpCnN1bShpcy5uYShCcmVhc3RDYW5jZXIpKQpgYGAKCuqysOy4oeqwkuydtCAxNuqwnOqwgCBCYXJlLm51Y2xlaSDsl5Ag7J6I64ukLmNvbXBsZXRlLmNhc2VzKCkg7ZWo7IiY64qUIO2VtOuLuSDtlonsnZgg66qo65OgIOqwkuydtCDroZzsp4DsiqTti7Eg7ZqM6reAIOuqqOuNuOydmCDqsJzrhZAgTkEg6rCAIOyVhOuLjCDqsr3smrAgVFJVRSwg7ZW064u5IO2WieydmCDqsJLsnbQg7ZWY64KY652864+EIE5BIOulvCDtj6ztlajtlZjqs6Ag7J6I64qUIOqyveyasCBGQUxTReqwkuydhCDrsJjtmZjtlZzri6QuIOyXrOq4sOyEnCBjb21wbGV0ZS5jYXNlc+ulvCDsnbTsmqntlZjsl6wgTkEg6rCAIOuTpOyWtOyeiOuKlCDtlonsnYQg65Oc656N7Iuc7YKo64ukLgoKYGBge3J9CkJyZWFzdENhbmNlcjI8LUJyZWFzdENhbmNlcltjb21wbGV0ZS5jYXNlcyhCcmVhc3RDYW5jZXIpLF0KCiMgTkEg7Jes67aA66W8IO2ZleyduOOEtwpzdW0oaXMubmEoQnJlYXN0Q2FuY2VyMikpCmBgYAoKCgoqKuykkeuztSDrjbDsnbTthLAg7ZmV7J24IOuwjyDsoJzqsbAqKgoKYGBge3J9Cm5yb3coQnJlYXN0Q2FuY2VyMikKCnN1bShkdXBsaWNhdGVkKEJyZWFzdENhbmNlcjIpKQoKYGBgCjY4M+2VrSDspJHsl5DshJwgOO2VreydtCDspJHrs7XrkJjsl4jsnYzsnYQg7JWM7IiYIOyeiOuLpC4g7KSR67O1IOygnOqxsAoKCmBgYHtyfQpCcmVhc3RDYW5jZXIzPC1CcmVhc3RDYW5jZXIyWyFkdXBsaWNhdGVkKEJyZWFzdENhbmNlcjIpLF0KbnJvdyhCcmVhc3RDYW5jZXIzKQpzdW0oZHVwbGljYXRlZChCcmVhc3RDYW5jZXIzKSkKYGBgCgoKKio0LiDrsJjsnZHrs4DsiJgg6rWs7ISxIOu2hO2PrCDtmZXsnbgqKgoKCmBgYHtyfQp0YWJsZShCcmVhc3RDYW5jZXIzJENsYXNzKQpjYXQoInRvdGFsIDoiLCBtYXJnaW4udGFibGUodGFibGUoQnJlYXN0Q2FuY2VyMyRDbGFzcykpKQoKCnByb3AudGFibGUodGFibGUoQnJlYXN0Q2FuY2VyMyRDbGFzcykpCmBgYArsnKDrsKnslZQg642w7J207YSwIOyghOyymOumrCDtm4Qg67CY7J2R67OA7IiYIENsYXNzIOydmCDrtoTtlaDtkZzrpbwg67O066m0IGJlZ2lu7J20IDQzOeqwnCwgbWFsaWduYW506rCAIDIzNuqwnOyduCDqsoPsnYQg67O87IiYIOyeiOuLpC4gCgoKKio1LiDshKTrqoXrs4DsiJgg6rCEIOuLpOykkeqzteyEoOyEsShNdWx0aWNvbGxpbmVhcml0eSkg7ZmV7J247J2EIOychO2VtCDrsJjsnZHrs4DsiJggQ2xhc3Mg66W8IFksIOyEpOuqheuzgOyImOulvCBY652864qUIOuNsOydtO2EsO2UhOugiOyehOycvOuhnCDrtoTrpqztlZjqs6AsIOyEpOuqheuzgOyImOydmCDtg4DsnoXsnYQg7Iir7J6Q7YOA7J6F7Jy866GcIOuzgO2ZmO2VnOuLpCoqCgoKYGBge3J9CiMgWSBhc3NpZ25lZCAwIGFzIGJlZ2luLCAxIGFzIG1hbGlnbmFudApZPC1pZmVsc2UoQnJlYXN0Q2FuY2VyMyRDbGFzcyA9PSdtYWxpZ25hbnQnLCAxLCAwKQoKIyBjaG9vc2UgdGhlIDJuZCB0byAxMHRoIGNvbHVtbnMgZnJvbSBCcmVhc3RDYW5jZXIzClg8LUJyZWFzdENhbmNlcjNbLCBjKDI6MTApXQoKIyDsg4jroZwg7IOd6ri0IOyEpOuqheuzgOyImCBY65Ok7J2YIO2DgOyeheydhCDsiKvsnpDtmJXsnLzroZwg67OA7ZiVCgpYJENsLnRoaWNrbmVzcyA8LWFzLmludGVnZXIoWCRDbC50aGlja25lc3MpClgkQ2VsbC5zaXplIDwtYXMuaW50ZWdlcihYJENlbGwuc2l6ZSkKWCRDZWxsLnNoYXBlIDwtYXMuaW50ZWdlcihYJENlbGwuc2hhcGUpClgkTWFyZy5hZGhlc2lvbiA8LWFzLmludGVnZXIoWCRNYXJnLmFkaGVzaW9uKQpYJEVwaXRoLmMuc2l6ZSA8LWFzLmludGVnZXIoWCRFcGl0aC5jLnNpemUpClgkQmFyZS5udWNsZWkgPC1hcy5pbnRlZ2VyKFgkQmFyZS5udWNsZWkpClgkQmwuY3JvbWF0aW4gPC1hcy5pbnRlZ2VyKFgkQmwuY3JvbWF0aW4pClgkTm9ybWFsLm51Y2xlb2xpIDwtYXMuaW50ZWdlcihYJE5vcm1hbC5udWNsZW9saSkKWCRNaXRvc2VzIDwtYXMuaW50ZWdlcihYJE1pdG9zZXMpCmBgYAoK64uk7J2M7J2AIOyEpOuqheuzgOyImCDqsITsnZgg64uk7KSR6rO17ISg7ISx7J20IOyhtOyerO2VmOuKlOyngOulvCDtmZXsnbjtlZjquLAg7JyE7ZW0IOyCsOygkOuPhCBzY2F0dGVyIHBsb3QsIOyDgeq0gOqzhOyImCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCwg67aE7IKw7Yy97LC97KeA7IiYIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3IsIOulvCDtmZXsnbjtlZzri6QuIOydtOqyg+ydgCDtmozqt4Drqqjrjbjsl5DshJwg7ISk66qF67OA7IiYIOqwhCDrj4Xrpr3shLHsnYQg6rCA7KCV7ZWY6riwIOuVjOusuOydtOuLpC4gCgoKCgoqKjYuIOyEpOuqheuzgOyImCDqsITsnZgg7IKw7KCQ64+E7JmAIOyDgeq0gOqzhOyImCDtmZXsnbjtlZjquLAqKgoKYGBge3J9CmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpCmNoYXJ0LkNvcnJlbGF0aW9uKFgsIGhpc3RvZ3JhbSA9IFRSVUUsIGNvbCA9ICdncmF5MTAnLCBwY2ggPTEpCmBgYAoKCuu2hOyEneqysOqzvCDrp6TsnbQg64aS7J2AIOyDgeq0gOq0gOqzhCAwLjkg7J207IOBIOydhCDrs7TsnbTripQg7ISk66qF67OA7IiYIENlbGwuc2l6ZSDsmYAgQ2VsbC5zaGFwZeqwgCDsobTsnqztlajsnLzroZwg64uk7KSR6rO17ISg7ISx7J2EIOydmOyLrO2VoOyImCDsnojri6QuIAoKR0dhbGx5IO2MqO2CpOyngOydmCBnZ2NvcnIoKSDtlajsiJjripQg7ISk66qF67OA7IiYIOqwhOydmCDsg4HqtIDqs4TsiJgg7Z6I7Yq466e17J2EIOyLnOqwge2ZlO2VnOuLpC4gCgpgYGB7cn0KbGlicmFyeShHR2FsbHkpCmdnY29ycihYLCBuYW1lID0gJ2NvcnJlbGF0aW9uJywgbGFiZWwgPSBUKQpgYGAK67aE7ISd6rKw6rO8IENlbGwuc2l6ZSDsmYAgQ2VsbC5zaGFwZSDrkZAg7IaN7ISx7J2YIOyDgeq0gOqzhOyImOqwgCAwLjnroZwg66ek7JqwIOuGkuydgCDsg4HqtIDqtIDqs4TqsIAg7J6I64uk64qUIOqyg+ydhCDslYwg7IiYIOyeiOuLpC4gCgoKKio4LiBmbXNiIO2MqO2CpOyngOydmCBWSUYg7ZWo7IiY66GcIOyEpOuqheuzgOyImCDqsITsnZgg67aE7IKw7Yy97LC97KeA7IiY66W8IO2ZleyduO2VmOq4sCoqCgpgYGB7cn0KbGlicmFyeShmbXNiKQoKVklGKGxtKENsLnRoaWNrbmVzcyB+IC4sIGRhdGE9WCkpClZJRihsbShDZWxsLnNpemUgfiAuLCBkYXRhPVgpKQpWSUYobG0oQ2VsbC5zaGFwZSB+IC4sIGRhdGE9WCkpClZJRihsbShNYXJnLmFkaGVzaW9uICB+IC4sIGRhdGE9WCkpClZJRihsbShFcGl0aC5jLnNpemUgfiAuLCBkYXRhPVgpKQpWSUYobG0oQmFyZS5udWNsZWkgfiAuLCBkYXRhPVgpKQpWSUYobG0oQmwuY3JvbWF0aW4gfiAuLCBkYXRhPVgpKQpWSUYobG0oTm9ybWFsLm51Y2xlb2xpIH4gLiwgZGF0YT1YKSkKVklGKGxtKE1pdG9zZXMgfiAuLCBkYXRhPVgpKQoKYGBgCuu2hOyEnSDqsrDqs7wg67aE7IKw7Yy97LC97KeA7IiYIOqwkuydtCAxMCDsnbTsg4Hsnbgg7ISk66qF67OA7IiY64qUIOyXhuuKlCDqsoPsnYQg67O8IOyImCDsnojri6QuIApmbXNiIO2MqO2CpOyngOydmCBWSUYg7ZWo7IiY64qUIOu2hOyCsO2MveywveyngOyImOulvCDstpzroKXtlZzri6QuIAoKCioqOS4g7ISk66qF67OA7IiYIO2RnOykgO2ZlO2VmOq4sCoqCgpgYGB7cn0KWDI8LXNjYWxlKFgpCnZhcihYMlssXSkKYGBgCgrsi6Ttlokg6rKw6rO8IOu2hOyCsOydtCAx7J246rKD7J2EIOuzvCDsiJgg7J6I64ukLiBzY2FsZSgp7ZWo7IiY64qUIOyEpOuqheuzgOyImOulvCDtj4nqt6DsnbQgMCwg67aE7IKw7J20IDHsnbjqsJLsnLzroZwg7ZGc7KSA7ZmU7ZWc64ukLiDthYzsnbTthLDrpbwg7ZGc7KSA7ZmU7ZWY66m0IOuzgOyImOydmCDsuKHsoJUg64uo7JyEIOuYkOuKlCDrspTsnITqsITsnZgg7Y647LCo66W8IOyXhuyVoOykgOuLpC4gCgoKKioxMC4g642w7J207YSw7IWL7J2EIO2biOugqOyaqSDrjbDsnbTthLAgODAgJSDsmYAg7YWM7Iqk7Yq4IOuNsOydtO2EsCAyMCAlIOuhnCDrtoTrpqztlZjquLAgKioKYGBge3J9CkJyZWFzdENhbmNlcjQ8LSBkYXRhLmZyYW1lKFksIFgyKQpzZXQuc2VlZCgxMjMpCgp0cmFpbjwtc2FtcGxlKDE6bnJvdyhCcmVhc3RDYW5jZXI0KSwgc2l6ZSA9ICAwLjggKiBucm93KEJyZWFzdENhbmNlcjQpLCByZXBsYWNlID0gRikKdGVzdDwtKC10cmFpbikKWS50ZXN0PC1ZW3Rlc3RdCgpzY2FsZXM6OnBlcmNlbnQobGVuZ3RoKHRyYWluKS9ucm93KEJyZWFzdENhbmNlcjQpKQpgYGAKCu2biOugqOuNsOydtO2EsOyZgCDthYzsiqTtirgg642w7J207YSw64qUIDgwJSwgMjAlIOuhnCDrtoTrpqzrkJzqsoPsnYQg7JWMIOyImCDsnojri6QuIAoKCioqMTIuIO2biOugqOyaqSDrjbDsnbTthLDroZwg66Gc7KeA7Iqk7YuxIO2ajOq3gCDrqqjrjbgg7KCB7ZWp7ZWY6riwKioKCmBgYHtyfQpnbG0uZml0PC1nbG0oWX4uLCBkYXRhID0gQnJlYXN0Q2FuY2VyNFt0cmFpbixdLCBmYW1pbHkgPSBiaW5vbWlhbCkKc3VtbWFyeShnbG0uZml0KQpgYGAKCioq7JiB7Zal66Cl7J20IOyXhuuKlCDrs4DsiJjrpbwg7KCc6rGw7ZWY6riw7JyE7ZW0IO2bhOynhOygnOqxsOuyleydhCDsgqzsmqntlZjsl6wg66qo64247J2EIOygge2Vqe2VmOq4sCoqCgpgYGB7cn0Kc3RlcChnbG0uZml0LCBkaXJlY3Rpb24gPSAnYmFja3dhcmQnKQoKYGBgCgoK7ZuE7KeE7KCc6rGw67KV7J2EIO2Gte2VmOyXrCBBSUMg6rCAIDk5Ljkx66GcIOqwgOyepeyekeydgCBnbG0oZm9ybXVsYSA9IFkgfiBDbC50aGlja25lc3MgKyBDZWxsLnNoYXBlICsgTWFyZy5hZGhlc2lvbiArIEJhcmUubnVjbGVpICsgQmwuY3JvbWF0aW4gKyBOb3JtYWwubnVjbGVvbGkgKyBNaXRvc2VzLCBmYW1pbHkgPSBiaW5vbWlhbCwgCmRhdGEgPSBCcmVhc3RDYW5jZXI0W3RyYWluLCBdKSDsnbQg6rCA7J6lIOyasOyImO2VnCDrqqjrjbjsnbTrnbzqs6Ag7YyQ64uo7ZWgIOyImCDsnojri6QuCgpgYGB7cn0KCgpgYGAKCgoqKjE0LiDtm4Tsp4TsoJzqsbDrspXsl5DshJwg7LGE7YOd65CcIOuqqOuNuOydhCDsoIHtlantlZjqs6Ag66qo64247J2YIOycoOydmCDqsoDsoJXtlZjquLAgKioKYGBge3J9CmdsbS5maXQuMjwtZ2xtKCBZIH4gQ2wudGhpY2tuZXNzICsgQ2VsbC5zaGFwZSArIE1hcmcuYWRoZXNpb24gKyAKICAgIEJhcmUubnVjbGVpICsgQmwuY3JvbWF0aW4gKyBOb3JtYWwubnVjbGVvbGkgKyBNaXRvc2VzLCBmYW1pbHkgPSBiaW5vbWlhbCwgCiAgICBkYXRhID0gQnJlYXN0Q2FuY2VyNFt0cmFpbiwgXSkKCnN1bW1hcnkoZ2xtLmZpdC4yKQoKYW5vdmEoZ2xtLmZpdC4yLHRlc3QgPSAnQ2hpc3EnKQpgYGAK7JyE7J2YIOy1nOyihSDrqqjrjbjroZzrtoDthLAg64uk66W4IOyEpOuqheuzgOyImOqwgCDthrXsoJzrkJjsl4jsnYQg6rK97JqwIOyEpOuqheuzgOyImCBYaSDqsIAg7ZWcIOuLqOychCDspp3qsIDtlaDrlYwg7Jyg67Cp7JWUIOyVheyEseydvCDtmZXrpaDsnZgg7Jik7KaI64qUIGV4cChiZXRhKGkpKSDrp4ztgbwg7Kad6rCA7ZWc64ukLiDsmIjroZwgTWl0b3Nlc+ydmCDtmozqt4Dqs4TsiJjqsIAgMC45NiDsnbTrr4DroZwgTWl0b3NlcyDsho3shLHsnbQg7ZWc64uo7JyEIOymneqwgO2VoCDrlYwg7Jyg67Cp7JWU7J20IOyVheyEseydmCDtmZXrpaDsnZgg7Jik7KaI64qUIGV4cCgwLjk2KSA9IDIuNjIg67CwIOymneqwgO2VnOuLpC4K66qo65OgIOyEpOuqheuzgOyImOydmCBwLXZhbHVlKHByb2IgQ2hpKeqwgCAwLjA1IOuztOuLpCDsnpHslYQg66qo64247J20IOycoOydmOuvuO2VmOuLpOuKlCDqsrDroaDsnYQg64+E7Lac7ZWgIOyImCDsnojri6QuIGFub3ZhIO2VqOyImOuKlCDtlZjrgpgg7J207IOB7J2YIOygge2VqeuQnCDrqqjrjbjsl5Ag64yA7ZWcIOu2hOyCsOu2hOyEneydhCDsiJjtlbTtlZzri6QuIOydtOuKlCDrqqjrjbjsnYQg7Y+J6rCA7ZWY6rGw64KYIOuqqOuNuOqwhOydmCDruYTqtZDrpbwg7JyE7ZW0IOyCrOyaqeuQnOuLpC4KCgoqKu2FjOyKpO2KuCDrjbDsnbTthLDroZwg66qo6424IOyEseuKpSDtj4nqsIAg7IiY7ZaJ7ZWY6riwICoqCgpgYGB7cn0KZ2xtLnByb2JzPC1wcmVkaWN0KGdsbS5maXQuMiwgQnJlYXN0Q2FuY2VyNFt0ZXN0LCBdLCB0eXBlID0gJ3Jlc3BvbnNlJykKCiMgcHJvYmFiaWxpdHkg7ZiV7YOc7J2YIOyYiOy4oSAKaGVhZChnbG0ucHJvYnMpCgojIHRocmVzaG9sZCDrpbwgMC4166GcIOyEpOyglQpnbG0ucHJlZDwtaWZlbHNlKGdsbS5wcm9icyA+IDAuNSwgMSwgMCkKCnRhYmxlKFkudGVzdCwgZ2xtLnByZWQpCgojIEFjY3VyYWN5LiBsb2dpYyDrs4DsiJjripQgbWVhbuycvOuhnCDstpTstpwgCm1lYW4oWS50ZXN0ID09IGdsbS5wcmVkKQoKIyBFcnJvciByYXRlCm1lYW4oWS50ZXN0ICE9IGdsbS5wcmVkKQpgYGAKCgoqKjE2LiBST0Mg6re4656Y7ZSE7JmAIEFVQyDtmZXsnbjtlZjquLAgKioKCmBgYHtyfQpsaWJyYXJ5KFJPQ1IpCnByIDwtIHByZWRpY3Rpb24oZ2xtLnByb2JzLCBZLnRlc3QpCnByZiA8LSBwZXJmb3JtYW5jZShwciwgbWVhc3VyZSA9ICd0cHInLCB4Lm1lYXN1cmUgPSAnZnByJykKcGxvdChwcmYsIG1haW4gPSAnUk9DIEN1cnZlJykKCgphdWMgPC0gcGVyZm9ybWFuY2UocHIsIG1lYXN1cmUgPSAnYXVjJykKYXVjPC1hdWNAeS52YWx1ZXNbWzFdXQphdWMKYGBgCgrrtoTshJ3qsrDqs7wgUk9DIOqzoeyEoOydtCDrp6TsnbQg7J207IOB7KCB7J206rOgIEFVQ+qwgCDslb0gMC45OSDroZwg66ek7JqwIOyasOyImO2VnCDrqqjrjbjsnbTrnbwg7ZWgIOyImCDsnojri6QuCgoK7LC46rOgOiBWSUYg6rCS7J20IDUg7J207IOB7J2066m0IOuLpOykkeqzteyEoOyEseydtCDsnojri6Tqs6Ag67O06rOgLCBWSUYg6rCS7J20IDEwIOydtOyDgeydtOuptCDri6TspJHqs7XshKDshLHsnbQg66ek7JqwIOyLrOqwge2VmOuLpOqzoCDtj4nqsIDtlZzri6QuIAoKCgojIyA2LjQg7J246rO17Iug6rK966edIAoK7J246rO17Iug6rK966edIOuqqOuNuDog7IOd66y87LK07J2YIOuHjOyLoOqyveqzhOulvCDrqqjrsKntlZjsl6wg7J6F66Cl7Iug7Zi47JmAIOy2nOugpeyLoO2YuCDqsITsnZgg6rSA6rOE66W8IOuqqOuNuO2ZlO2VmOuKlCDquLDrspXsnbTri6QuIAoK64uo7Li17Iug6rK966edOiDsnoXroKXsuLXsnbQg7J2A64uJ7Li17J2EIOqxsOy5mOyngCDslYrqs6Ag7KeB7KCRIOy2nOugpey4teyXkCDsl7DqsrAgKOyYpOuhnOyngCDsnoXroKXsuLXqs7wg7Lac66Cl7Li17Jy866Gc66eMIOq1rOyEsSDrkJjslrQg7J6I64ukLikKCuuLpOy4teyLoOqyveunnTog64uo7Li17Iug6rKw66edIOuqqOuNuOqzvCDri6zrpqwg7J6F66Cl7Li16rO8IOy2nOugpey4tSDsgqzsnbTsl5Ag7Jes65+sIOqwnOydmCDsnYDri4nsuLXsnYQg6rCA7KeIIOyImCDsnojri6QuCgrtlLzrk5ztj6zsm4zrk5zsi6Dqsr3rp506IOygleuztOqwgCDsoITrsKnsnLzroZwg7KCE64us65CY64qUIOqyg+ydgCDsg53rrLztlZnsoIEg7Iug6rK96rOE7JeQ7ISc64+EIOycoOyCrO2VmOqyjCDrgpjtg4DrgpjrqbAsIOydtOufrO2VnCDsm5Drpqzrpbwg7J246rO17Iug6rK966ed7JeQIOyggeyaqe2VnCDqsoPsnYQg66eQ7ZWc64ukLgoKZHVwaWNhdGVkIO2VqOyImOuKlCDspJHrs7Ug6rCS7J20IOyhtOyerO2VmOuKlCDqsr3smrAgVFJVRSDslYTri4wg6rK97JqwIEZBTFNF66GcIOqwkuydhCDstpzroKXtlZzri6QuCgpHR2FsbHkg7Yyo7YKk7KeA7J2YIGdnY29yciDtlajsiJjripQg7ISk66qF67OA7IiYIOqwhOydmCDsg4HqtIDqs4TsiJgg7Z6I7Yq466e17J2EIOyLnOqwge2ZlO2VnOuLpC4KCmZtc2Ig7Yyo7YKk7KeA7J2YIFZJRiDtlajsiJjripQg67aE7IKw7Yy97LC97KeA7IiY66W8IOy2lOugpe2VnOuLpAoKc2NhbGUg7ZWo7IiY64qUIOyEpOuqheuzgOyImOulvCDtj4nqt6DsnbQgMCwg67aE7IKw7J20IDEg7J246rCS7Jy866GcIO2RnOykgO2ZlO2VnOuLpC4gCgpubmV0IO2VqOyImOuKlCDtmZzshLHtlajsiJjroZwg7Iuc6re466qo7J2065OcIOuYkOuKlCDshKDtmJUg7Lac66Cl7J2EIOyCrOyaqe2VnOuLpC4gCgpuZXVyYWxuZXQg7ZWo7IiY64qUIOuLpOyWke2VnCDsl63soITtjIwg7JWM6rOg66as7KaY7J2EIO2Gte2VtCDrqqjrjbjsnYQg7IOd7ISx7ZWc64ukLgoKY29tcHV0ZSDtlajsiJjripQg6rCBIOuJtOufsOydmCDstpzroKXqsJLsnYQg6rOE7IKw7ZWc64ukLiAKCgoKIyMgNi40LjEg7J246rO17Iug6rK966edIOuqqOuNuOydmCDqsJzrhZAKCuyduOqzteyLoOqyveunnSBBTk4g66qo64247J2AIOyDneusvOyytOydmCDrh4zsi6Dqsr3qs4Trpbwg66qo67Cp7ZWY7JesIOyeheugpeyLoO2YuOyZgCDstpzroKUg7Iug7Zi46rCE7J2YIOq0gOqzhOulvCDrqqjrjbjtmZTtlZjripQg6riw67KV7J2064ukLiAKCuyekOyXsCDribTrn7DsnbQg7Iuc64OF7Iqk66W8IO2Gte2VtCDsi6DtmLjrpbwg7KCE64us67Cb64qUIOqzvOygleyXkOyEnCDsi6DtmLjsnZgg6rCV64+E6rCAIOq4sOykgOy5mOuztOuLpCDtgazrqbQg64m065+w7J2AIO2ZnOyEse2ZlOuQmOqzoCDsi6Dqsr3rj4zquLDrpbwg7Ya17ZWY7JesIOyLoO2YuOulvCDrsKnstpztlZzri6QuIAoK7Jes6riw7IScIOyeheugpeydgCDsi5zrg4XsiqTsl5Ag7ZW064u57ZWY6rOgLCDqsJzrs4Qg7Iug7Zi47J2YIOqwleuPhOyXkCDrlLDrnbwg6rCA7KSR65CY66mwLCDtmZzshLHtlajsiJjripQg7Lac66Cl7J2EIOqzhOyCsO2VnOuLpC4gCgoKIyMgNi40LjIg64uo7Li17Iug6rK966edL+uLqOy4te2NvOyFie2KuOuhoAoK64uo7Li17Iug6rK966ed7J2AIOyeheugpey4teydtCDsnYDri4nsuLXsnYQg6rGw7LmY7KeAIOyViuqzoCDsp4HsoJEg7Lac66Cl7Li17JeQIOyXsOqysOuQmOyWtCDsnojri6QuIOyeheugpey4teqzvCDstpzroKXsuLXsnLzroZzrp4wg6rWs7ISxIOuQmOyWtOyeiOuLpC4gCgrsnbjqs7Xsi6Dqsr3rp53snYAg66eO7J2AIOuNsOydtO2EsOyXkCDrjIDtlbQg7ZWZ7Iq17J2EIOqxsOyzkCDsm5DtlZjripQg6rKw6rO86rCAIOuCmOyYpOuPhOuhnSAo7Jik7LCo6rCAIOyekeyVhOyngOuKlCDrsKntlqXsnLzroZwgKSDqsIDspJHsuZjqsIAg7KGw7KCV65Cc64ukLiDspoksIOyduOqzteyLoOqyveunneydgCDqsIDspJHsuZjrpbwg67CY67O17KCB7Jy866GcIOyhsOygle2VmOupsCDtlZnsirXtlZzri6QuIAoKCiMjIDYuNC4zIOuLpOy4teyLoOqyveunnSAKCuuLpOy4teyLoOqyveunnSDrmJDripQg64uk7Li17Y287IWJ7Yq466GgIOydgCDri6jsuLXsi6Dqsr3rp53qs7wg64us66asIOyeheugpey4teqzvCDstpzroKXsuLUg7IKs7J207JeQIOyXrOufrCDqsJzsnZgg7J2A64uJ7Li17J2EIOqwgOyniCDsiJgg7J6I64ukLiDsnYDri4nsuLXsnZgg7IiY64qUIOydmOyCrOqysOyglSDqsr3qs4Trpbwg7KCV7ZWY64qU642wIOykkeyalO2VmOuLpC4gCuydgOuLiey4teydmCDsiJjrpbwg7KCV7ZWg65WMIOqzoOugpOyCrO2VreydgCAKCirri6TsuLXsi6Dqsr3rp53snYAg64uo7Li17Iug6rK966ed7JeQIOu5hO2VtCDtm4jroKjsnbQg7Ja066C164ukLioKKuyLnOq3uOuqqOydtOuTnCDtlajsiJjrpbwg6rCA7KeA64qUIDLqsJwg7Li17J2YIOuEpO2KuOybjO2BrCgx6rCcIOydgOuLiey4tSnripQg7J6E7J2Y7J2YIOydmOyCrOqysOyglSDqsr3qs4Trpbwg66qo64247ZmU7ZWgIOyImCDsnojri6QuICoKCuqwgSDsuLXsnZgg64W465OcIOyImCAodW5pdHMpIOuKlCDri6TsnYzsnYQg6rKw7KCV7ZWY7JesIOqysOygle2VnOuLpC4KCirstpzroKXsuLUg64W465OcIC0gb3V0cHV0IHVuaXRzIOydmCDsiJjripQg7Lac66ClIOuylOyjvOydmCDsiJjroZwg6rKw7KCV7ZWc64ukLiAqCirsnoXroKUgLSBpbnB1dHMg7J2YIOyImOuKlCDsnoXroKUg7LCo7JuQ7J2YIOyImOuhnCDqsrDsoJXtlZzri6QuKgoq7J2A64uJ7Li1IOuFuOuTnCAtIGhpZGRlbiB1bml0cyDsnZgg7IiY64qUIOuLpOydjOydhCDqs6DroKTtlZjsl6wg7KCV7ZWc64ukLuuEiOustCDsoIHsnLzrqbQg64Sk7Yq47JuM7YGs6rCAIOuzteyeoe2VnCDsnZjsgqzqsrDsoJUg6rK96rOE66W8IOunjOuTpOyImCDsl4bri6QsIOuEiOustCDrp47snLzrqbQg64Sk7Yq47JuM7YGs7J2YIOydvOuwmO2ZlOqwgCDslrTroLXri6QuKgoKCiMjIDYuNC40IO2UvOuTnOuTnO2PrOybjOuTnOyLoOqyveunnSBGTk4KCkZlZWRmb3J3YXJkIE5ldXJhbCBOZXR3b3JrKEZOTik6IOygleuztOqwgCDsoITrsKnsnLzroZwg7KCE64us65CY64qUIOyduOqzteyLoOqyveunnSwg7Iug6rK97IS47Y+s66W8IOyYpOyngSDslZ4g67Cp7Zal7Jy866Gc66eMIOyXsOqysOyLnO2CqOuLpC4g7Ja065akIOyLoOqyveyEuO2PrCDsuLXrj4Qg7J207KCE7J2YIOyLoOqyveyEuO2PrCDsuLXqs7zripQg64us66asIOyXsOqysOuQmOyngCDslYrquLAg65WM66y47JeQIO2UvOuTnO2PrOybjOuTnOudvOuKlCDsnbTrpoTsnYQg6rCW6rKMIOuQmOyXiOuLpC4KCu2UvOuTnO2PrOybjOuTnOyLoOqyveunneyXkOyEnCDsnoXroKXsuLXsnYAg7J6F66ClIOuNsOydtO2EsOulvCDrsJvslYTrk6TsnbTripQg6riw64ql7J206rOgLCDsnoXroKXsuLXsnZgg64m065+wIOuYkOuKlCDrhbjrk5wg6rCc7IiY64qUIOyeheugpSDrjbDsnbTthLDsnZgg7Yq57ISxIOqwnOyImOyZgCDsnbzsuZjtlZjri6QuIAoK7J2A64uJ7Li17JeQ7IScIOuJtOufsCDsiJjqsIAg64SI66y0IOunjuycvOuptCDqs7zsoIHtlansnbQg67Cc7IOd7ZWY6rOgLCDribTrn7Ag7IiY6rCAIOuEiOustCDsoIHsnLzrqbQg7J6F66ClIOuNsOydtO2EsOulvCDstqnrtoTtnogg7ZGc7ZiE66q77ZWY64qUIOqyveyasOqwgCDrsJzsg53tlaAg7IiYIOyeiOuLpC4g7J2A64uJ7Li17J2YIO2ZnOyEse2ZlCDtlajsiJjripQg64m065+w7Jy866GcIOuqqOyVhOynhCDsi6DtmLjrpbwg7KKAIOuNlCDrs4Drs4TroKUg7J6I64qUIOyDge2DnOuhnCDsoITtmZjtlZjripQg6rKD7J2064ukLiAKCu2VmeyKtShsZWFybmluZyk6IO2UvOuTnO2PrOybjOuTnOyLoOqyveunneyXkOyEnCDsm5DtlZjripQg6rKw6rO866W8IOyWu+q4sCDsnITtlbTshJzripQg64m065+wIOyCrOydtOyXkCDsoJXrs7Qg7KCE64usIOqzvOygleyXkCDsnpHsmqntlZjripQg7KCB64u57ZWcIOqwgOykkey5mOulvCDslYzslYTrgrTslbwg7ZWY64qU642wIOydtOqyg+ydhCDtlZnsirXsnbTrnbzqs6Ag7ZWc64ukLiAKCuyXreyghO2MjChCYWNrIFByb3BhZ2F0aW9uKSDripQg64yA7ZGc7KCB7J24IOyngOuPhO2VmeyKtSDslYzqs6DrpqzsppjsnLzroZwg66CI7J2067iU65CcIO2VmeyKtSDrjbDsnbTthLDrpbwg6rCA7KeA6rOgIOyXrOufrCDqsJzsnZgg7J2A64uJ7Li17J2EIOqwgOyngOuKlCDtlLzrk5ztj6zsm4zrk5zsi6Dqsr3rp53snYQg7ZWZ7Iq17Iuc7YKsIOuVjCDsgqzsmqnrkJzri6QuIOydtOuKlCDtmITsnqwg7Iug6rK966ed7JeQ7IScIOqwgOyepSDrp47snbQg7IKs7Jqp65CY64qUIO2VmeyKtSDslYzqs6DrpqzsppjsnbTri6QuIAoKCioq7Jet7KCE7YyMIO2VmeyKteydgCDtgazqsowgMyDri6jqs4Qg6rO87KCV7J20IOuwmOuzteuQnOuLpC4qKgoKKjEuIO2UvOuTnO2PrOybjOuTnCDqs7zsoJUqCgrrqLzsoIAg66qo65OgIOy4teyXkCDsnojripQg6rCA7KSR7LmY66W8IOyehOydmOydmCDsiJjroZwg7LSI6riw7ZmU7ZWY6rOgLCDroIjsnbTruJTrkJwg7ZWZ7Iq1IOuNsOydtO2EsOulvCDsnoXroKXsuLXsl5DshJwg7J6F66Cl67Cb7JWEIOydgOuLiey4teydhCDthrXtlbQg7Lac66Cl7Li16rmM7KeAIO2UvOuTnO2PrOybjOuTnCDtlZzri6QuIAoKKjIuIOyXreyghO2MjCDqs4TsgrAqCgrtlLzrk5ztj6zsm4zrk5zrkJwg7JiI7IOB6rCS6rO8IOyLpOygnOqwkuydmCDssKjsnbTsnbgg7JeQ65+s66W8IOq1rO2VmOqzoCwg7JeQ65+s66W8IOy1nOyGjO2ZlO2VmOuKlCDqsIDspJHsuZjrpbwg7LC+64qUIOqzvOygleydtOuLpC4gCgoqMy4g6rCA7KSR7LmYIOyhsOyglSoKCuqwgOykkey5mCDsobDsoJU6IOyVniDri6jqs4Tsl5DshJwg6rOE7IKw65CcIOyXkOufrOuhnCDtlZnsirXrpaAo7LWc7IaM7Y+J6reg7KCc6rOx7J2YIOuvuOu2hOqwkuydhCDsnbTsmqntlbQg7ZWZ7Iq166Wg7J2EIOyEoOyglSkg66eM7YG8IOyImOygleuQnCDqsIDspJHsuZjrpbwg6rWs7ZWY6rOgLCDrjbjtg4Ag66Ow7J2EIOydtOyYge2VtCDqsIDspJHsuZjrpbwg7KGw7KCV7ZWc64ukLiAKCgoqKuyduOqzteyLoOqyveOFh+unnSDrqqjrjbjsnZgg7J6lLCDri6jsoJAqKgoKKuyepeygkDoqCgrrs4DsiJjsnZgg7IiY6rCAIOunjuqxsOuCmCDsnoUsIOy2nOugpSDrs4DsiJgg6rCE7J2YIOuzteyekOuwmCDruYTshKDtmJUg66y47KCc7JeQ64+EIO2DgeyblO2VnCDshLHriqXsnYQg67O07J2464ukLgoK67aE66WYIOuwjyDsiJjsuZgg7JiI7LihIOusuOygnOyXkCDrqqjrkZAg7KCB7JqpIOqwgOuKpe2VmOuLpC4KCu2GteqzhOyggSDquLDrs7gg6rCA7KCV7J20IOyggeqzoCDsnKDsl7DtlZwg66qo64247J2EIOunjOuToOuLpC4KCuuNsOydtO2EsCDsgqzsnbTspojqsIAg7J6R6rGw64KYIOu2iOyZhOyghCDrjbDsnbTthLAsIOuFuOydtOymiCDrjbDsnbTthLDqsIAg7J6I64qUIOqyveyasOyXkOuPhCDri6Trpbgg66qo64247JeQIOu5hO2VmOyXrCDsmIjsuKHshLHriqXsnbQg7Jqw7IiY7ZWcIOqyveyasOqwgCDrp47ri6QuIAoKCirri6jsoJA6KgoK66qo6424IOqysOqzvCDtlbTshJ3snbQg7Ja066Ck7JuM7IScIOydgOuLiey4teydmCDrhbjrk5zrk6TsnbQg66y07JeH7J2EIO2RnO2YhO2VmOuKlOyngCwg6rKw6rO86rCSIOyEpOuqheydtCDtlYTsmpTtlZwg66qo642466eB7JeQ64qUIOygge2Vqe2VmOyngCDslYrri6QuIAoK7J2A64uJ7Li17J2YIOyImOyZgCDsnYDri4nrhbjrk5wg7IiY7J2YIOqysOygleydtCDslrTroLXri6QuCgrrgpjsnbTruIwg67Kg7J207KaIIOuhnOyngOyKpO2LsSDtmozqt4Ag66qo64247LKY65+8IOuztOuLpCDri6jsiJztlZwg67aE66WYIOuqqOuNuOyXkCDruYTtlbQg7Lu07ZOo7YyFIOyXsOyCsOyXkCDrp47snYAg7J6Q7JuQ7J20IO2VhOyalO2VmOuLpC4gCgrqs7zsoIHtlakg65iQ64qUIOqzvOyEnOygge2VqeydtCDrsJzsg53tlZjquLAg7Im964ukLgoK7LSI6riw6rCS7JeQIOuUsOudvCDsoITssrTsoIEg6rSA7KCQ7JeQ7ISc7J2YIOy1nOygge2VtOqwgCDslYTri4wg7KeA7JetIOy1nOygge2VtOqwgCDshKDtg53rkKAg7IiYIOyeiOuLpC4KCgoqKuunjuydtCDsgqzsmqnrkJjripQg7Zmc7ISx7ZWo7IiYKioKCuyLnOq3uOuqqOydtOuTnCDtlajsiJg6IOqysOqzvOuKlCDsl7Dsho3tmJXsnbTqs6AgMDw9eTw9MSDsnbTrqbAgLOuhnOyngOyKpO2LsSDtmozqt4Ag66qo64246rO8IOycoOyCrO2VmOuLpC4KeSA9IDEgLyBbMStleHAoLXopXQoK6rOE64uo7ZWo7IiYOiDqsrDqs7zripQg7J207KeE7ZiV7J24IDAg65iQ64qUIDEuCnkgPSAwIHdoZW4gejwwOyAgeSA9IDEgd2hlbiB6Pj0xCgp0YW5oIO2VqOyImDog6rKw6rO864qUIOyXsOyGje2YleydtOupsCAtMTw9eTw9MSDsnbTri6QuCnkgPSBbZXhwKHopLWV4cCgteildL1tleHAoeikrZXhwKC16KV0KCnJlbHUg7ZWo7IiYOiDsnoXroKXqsJLsnbQgMCDsnbTtlZjripQgMCwgMCDstIjqs7zripQgeCDqsJLsnYQg6rCA7KeA66mwIOy1nOq3vCDrlKXrn6zri53sl5DshJwg66eO7J20IOyCrOyaqeuQmOuKlCDtlajsiJjsnbTri6QuCllyZWx1ID0gMCwgaWYgeCA8PSAwIG9yIFlyZWx1ID0geCwgaWYgeCA+IDAgCgpzb2Z0bWF4IO2VqOyImDog7ZGc7KSA7ZmU7KO87IiYIOuYkOuKlCDsnbzrsJjtmZQg66Gc7KeA7Iqk7YuxIO2VqOyImOuhnOuPhCDrtojrpqzrqbAg7Lac66Cl6rCS7J20IOyXrOufrCDqsJzroZwg7KO87Ja07KeA6rOgLCAK66qp7ZGc7LmY6rCAIOuLpOuylOyjvOyduCDqsr3smrDsl5Ag6rCBIOuylOyjvOyXkCDsho3tlaAg7IKs7ZuEIO2ZleuloOydhCDsoJzqs7XtlZjripQg7ZWo7IiY7J2064ukLiAKCuqwgOyasOyKpCDtlajsiJg6IOqysOqzvOuKlCDsl7Dsho3tmJXsnbTrqbAgMCA8PSB5IDw9ICAxIOydtOuLpC4gCnkgPSBleHAoLXpeMiAvIDIpCgrrtoDtmLgg65iQ64qUIHRocmVzaG9sZCDtlajsiJg6IOqysOqzvOuKlCDsnbTsp4TtmJUgLTEg65iQ64qUIDEg7J2064ukLiAKeSA9IC0xICwgeiA8IDAgb3IgeSA9IDEsIHogPj0gMQoKCirssLjqs6A6IOyngOyXre2VtCAtIGxvY2FsIG1pbmltdW0qCgrsi6Dqsr3rp53snYAg6rCA7KSR7LmY66W8IOyehOydmOydmCDqsJLsnLzroZwg7LSI6riw7ZmU7ZWcIO2bhOyXkCDqsIDspJHsuZjrpbwg7KGw7KCI7ZWY66m07IScIOyXkOufrOydmCDsoJzqs7HtlZkgKHN1bSBvZiBzcXVyZWQgZXJyb3IpIOuYkOuKlCDsl5TtirjroZztlLzrpbwg6riw7KSA7Jy866GcIOy1nOygge2ZlO2VnOuLpC4g7J2064qUIOyImOyLneycvOuhnCDri6jrsojsl5Ag7LWc7KCB7J2YIOqwgOykkey5mOulvCDssL7ripQg6rKD7J20IOyWtOugteq4sCDrlYzrrLjsl5Ag67CY67O17KCB7Jy866GcIOuLteydhCDssL7slYTqsIDripQg6rKD7J2064ukLiDsp4Dsl63tlbQg66y47KCc64qUIOyXkOufrOulvCDstZzshoztmZTsi5ztgqTripQg7LWc7KCB7J2YIO2MjOudvOuvuO2EsOulvCDssL7ripQg66y47KCc7JeQIOyeiOyWtOyEnCDtjIzrnbzrr7jthLAg6rO16rCE7JeQIOyImOunjuydgCDsp4Dsl63soIHsnbgg7ZmA65Ok7J20IOyhtOyerO2VmOyXrCDsnbTrn6ztlZwg7KeA7Jet7ZW07JeQIOu5oOyniOqyveyasCDsoITsl63tlbQgKGdsb2JhbCBtaW5pbXVtKSDsnYQg7LC+6riwIO2emOuTpOqyjCDrkJjripQg66y47KCc66W8IOydvOy7q+uKlOuLpC4gCgojIyA2LjQuNSDsnbjqs7Xsi6Dqsr3rp50g67aE7ISdIOyYiOygnCB3aXRoIG5uZXQoKe2VqOyImCAKCuuLpOydjOydgCBubmV07ZWo7IiY66GcIGRhdGFzZXRzIO2MqO2CpOyngOydmCDsnpDsl7Ag7Jyg7IKw6rO8IOyduOqztSDsnKDsgrAg7ZuE7J2YIOu2iOyehOyXkCDrjIDtlZwg7IKs66GALeuMgOyhsCDsl7Dqtawg642w7J207YSw7IWL7J2EIOydtOyaqe2VtCDsi6Dqsr3rp50g66qo64247J2EIOygge2Vqe2VmOuKlCDsmIjsoJzri6QuIAoKCiMjIyAxLiBpbmZlciDrjbDsnbTthLDshYsg67aI66Gc7Jik6riwCgpgYGB7cn0KZGF0YSgnaW5mZXJ0JywgcGFja2FnZSA9ICdkYXRhc2V0cycpCnN0cihpbmZlcnQpCnRhYmxlKGluZmVydCRjYXNlKSAjIO2DgOq5gyDrs4DsiJgKYGBgCgoKCmluZmVydCDrjbDsnbTthLDripQgOOqwnOydmCDrs4DsiJjsmYAgMjQ46rCc7J2YIOq0gOy4oeqwkuydhCDqsIDsp4Dqs6Ag7J6I64ukLiDrsJjsnZHrs4DsiJggY2FzZeuKlCAxKOyCrOuhgCksIDAgKOuMgOyhsCnrpbwg64KY7YOA64K464ukLiDrsJjsnZHrs4DsiJjsnZgg67aE7ZWg7ZGc66W8IOuztOuptCAw7J20IDE2NeqwnCwgMeydtCA4M+qwnOyduCDqsoPsnYQg67O8IOyImCDsnojri6QuIAoKIyMjIDIuIOqysOy4oeqwkiwg7KSR67O1642w7J207YSwIO2ZleyduCDrsI8g7KCc6rGw7ZWY6riwIAoKYGBge3J9CiMg7Yag7YOIIE5BIApjb2xTdW1zKGlzLm5hKGluZmVydCkpCgojIO2GoO2DiCDtla3siJgKbnJvdyhpbmZlcnQpCgojIO2GoO2DiCDspJHrs7XrjbDsnbTthLAgCnN1bShkdXBsaWNhdGVkKGluZmVydCkpCgpgYGAK67aE7ISd6rKw6rO8IOqysOy4oeqwkuydgCDsobTsnqztlZjsp4Ag7JWK7JWY6rOgLCDspJHrs7Ug642w7J207YSw64qUIDMx6rCcIOyeiOyWtCDrqqjrkZAg7KCc6rGw7ZWc64ukLgoKYGBge3J9CiMg7KSR67O165CcIOuNsOydtO2EsOulvCDsoJzqsbDtlZjqs6AgaW5mZXJ0MiDrnbzqs6Ag7KeA7KCVCmluZmVydDI8LWluZmVydFshZHVwbGljYXRlZChpbmZlcnQpLCBdCgpucm93KGluZmVydDIpCgpzdW0oZHVwbGljYXRlZChpbmZlcnQyKSkKYGBgCgojIyMgMy4g67CY7J2R67OA7IiYIOq1rOyEsSDrtoTtj6wg7ZmV7J247ZWY6riwCgpgYGB7cn0KdGFibGUoaW5mZXJ0MiRjYXNlKTsgY2F0KCd0b3RhbDonLCBtYXJnaW4udGFibGUodGFibGUoaW5mZXJ0MiRjYXNlKSkpCgojIDAgPSDrjIDsobAgLCAxID0g7IKs66GAIApwcm9wLnRhYmxlKHRhYmxlKGluZmVydDIkY2FzZSkpCmBgYAoKaW5mZXJ0IOuNsOydtO2EsCDsoITsspjrpqwg7ZuEIOuwmOydkeuzgOyImOydmCDrtoTtlaDtkZzrpbwg67O066m0IDAo64yA7KGwKSDsnbQgMTM06rCcLCAxKOyCrOuhgCnsnbQgODPqsJzsnbgg6rKD7J2EIOuzvCDsiJjsnojri6QuIAoKIyMjIDQuIOyEpOuqheuzgOyImCDqsIQg64uk7KSR6rO17ISg7ISxIO2ZleyduOydhCDsnITtlbQg66qp7ZGc67OA7IiY66W8IFksIOyEpOuqheuzgOyImOulvCBY652864qUIOuNsOydtO2EsCDtlITroIjsnoTsnLzroZwg67aE66as7ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoZHBseXIpCgpZIDwtIGluZmVydDIkY2FzZQoKIyDshKTrqoXrs4DsiJgg7KSR7JeQ7IScIGVkdWNhdGlvbuyGjeyEseydhCDsoJzsmbjtlZwg64KY66i47KeAIDbqsJzrs4DsiJjrpbwg7ISg7YOd7ZWY7JesIFjsl5Ag7ZWg64u57ZWc64ukLgpYIDwtIGluZmVydDIgJT4lCiAgc2VsZWN0KCdhZ2UnLCdwYXJpdHknLCdpbmR1Y2VkJywnc3BvbnRhbmVvdXMnLCdzdHJhdHVtJywncG9vbGVkLnN0cmF0dW0nKQpgYGAgCgojIyMgNS4g7ISk66qF67OA7IiYIOqwhOydmCDsgrDsoJDrj4RzY2F0dGVyIHBsb3TsmYAg7IOB6rSA6rOE7IiY6rCSY29ycmVsYXRpb24gY29lZmZpY2llbnQg7ZmV7J247ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpCmNoYXJ0LkNvcnJlbGF0aW9uKFgsIGhpc3RvZ3JhbSA9IFRSVUUsIGNvbCA9ICdncmV5MTAnLCBwY2ggPSAxKQpgYGAKCuu2hOyEneqysOqzvCDrhpLsnYAg7IOB6rSA6rOE7IiYKDAuN+ydtOyDgSkg7J2EIOuztOydtOuKlCDshKTrqoXrs4DsiJjripQgc3RyYXR1bSwgcG9vbGVkLnN0cmF0dW0g6rCA7KG07J6s7ZWo7Jy866GcIOuLpOykkeqzteyEoOyEseydhCDsnZjsi6ztlaDsiJgg7J6I64ukLiAKCmBgYHtyfQpsaWJyYXJ5KEdHYWxseSkKZ2djb3JyKFgsIG5hbWUgPSAnY29ycmVsYXRpb24gY29lZmZpY2llbnQnLCBsYWJlbCA9IFQpCgpgYGAKCkdHYWxseSDtjKjtgqTsp4DsnZggZ2djb3Jy7ZWo7IiY66GcIOu2hOyEne2VnCDqsrDqs7wgc3RyYXR1bSwgcG9vbGVkLnN0cmF0dW0g65GQIOyGjeyEseydmCDsg4HqtIDqs4TsiJjqsIAgMC4366GcIOuGkuydgCDsg4HqtIDqtIDqs4TqsIAg7J6I64uk64qUIOqyg+ydhCDslYwg7IiYIOyeiOuLpC4gCgoKIyMjIDcuIOyEpOuqheuzgOyImCDqsITsnZgg67aE7IKw7Yy97LC97KeA7IiYIC0gVklGIO2ZleyduO2VmOq4sCAKYGBge3J9CmxpYnJhcnkoZm1zYikKVklGKGxtKGFnZSB+IC4sIGRhdGEgPSBYKSkKVklGKGxtKHBhcml0eSB+IC4sIGRhdGEgPSBYKSkKVklGKGxtKGluZHVjZWQgfiAuLCBkYXRhID0gWCkpClZJRihsbShzcG9udGFuZW91cyB+IC4sIGRhdGEgPSBYKSkKVklGKGxtKHN0cmF0dW0gfiAuLCBkYXRhID0gWCkpClZJRihsbShwb29sZWQuc3RyYXR1bSB+IC4sIGRhdGEgPSBYKSkKCgpgYGAKCuu2hOyEnSDqsrDqs7wg67aE7IKw7Yy97LC97KeA7IiY6rCS7J20IDXsnbTsg4Hsnbgg7ISk66qF67OA7IiY64qUIOyXhuuKlCDqsoPsnYQg67O8IOyImOyeiOuLpC4gVklG64qUIOuPheumveuzgOyImOqwgCDri6TspJEg6rO17IKw7ISxKE11bHRpY29sbG5lYXJpdHkp7J2YIOusuOygnOulvCDqsJbqs6Ag7J6I64qU7KeAIO2MkOuLqO2VmOuKlCDquLDspIDsnbTrqbAs7KO866GcIDEw67O064ukIO2BrOuptCDqt7gg64+F66a967OA7IiY64qUIOuLpOykkeqzteyCsOyEseydtCDsnojri6Tqs6Ag66eQ7ZWp64uI64ukLgoKIyMjIDguIOyEpOuqheuzgOyImCDtkZzspIDtmZTtlZjquLAKCmBgYHtyfQpYMjwtIHNjYWxlKFgpCnZhcihYMikKYGBgIAoK7Iuk7ZaJIOqysOqzvCDrtoTsgrDsnbQgMeyduOqyg+ydhCDrs7wg7IiYIOyeiOuLpC4gc2NhbGUg7ZWo7IiY64qUIOyEpOuqheuzgOyImOulvCDtj4nqt6DsnbQgMCwg67aE7IKw7J20IDHsnbgg6rCS7Jy866GcIO2RnOykgO2ZlO2VnOuLpC4gCgoKIyMjIDEwLiDrjbDsnbTthLDshYsg7J2EIO2biOugqOyaqSDrjbDsnbTthLDsmYAg7YWM7Iqk7Yq4IOuNsOydtO2EsOuhnCDrtoTrpqwgCgpgYGB7cn0KaW5mZXJ0LmRhdGE8LWRhdGEuZnJhbWUoWSxYMikKCnNldC5zZWVkKDEyMykKdHJhaW48LXNhbXBsZSgxOm5yb3coaW5mZXJ0LmRhdGEpLCBzaXplID0gMC44Km5yb3coaW5mZXJ0LmRhdGEpLCByZXBsYWNlID0gRikKdGVzdDwtKC10cmFpbikKWS50ZXN0PC1ZW3Rlc3RdCgpzY2FsZXM6OnBlcmNlbnQobGVuZ3RoKHRyYWluKS9ucm93KGluZmVydC5kYXRhKSkKaGVhZCh0cmFpbikKYGBgCgrsi6Ttlokg6rKw6rO8IOuNsOydtO2EsOyFi+yXkOyEnCA4MO2UhOuhnOqwgCDtm4jroKjsmqkg642w7J207YSw66GcIOu2hOumrOuQnOqyg+ydhCDslYwg7IiYIOyeiOuLpC4g7Jes6riw7IScIO2biOugqOyaqSDrjbDsnbTthLAodHJhaW4pLCDthYzsiqTtirgg642w7J207YSwKHRlc3QpLCDthYzsiqTtirgg67CY7J2R67OA7IiYIOuNsOydtO2EsCAoWS50ZXN0KSDsl5DripQg6rSA7Lih6rCS7J20IOuTpOyWtOyeiOuKlCDqsoPsnbQg7JWE64uI6528IOyduOuNseyKpCDsoJXrs7TqsIAg65Ok7Ja0IOyeiOuLpC4KCgojIyMgMTEuIO2biOugqOyaqSDrjbDsnbTthLDroZwg7J246rO17Iug6rK966edIOuqqOuNuOydhCDsoIHtlantlZjquLAgCgpgYGB7cn0KbGlicmFyeShubmV0KQoKbm4uZml0PC1ubmV0KFkgfiBzcG9udGFuZW91cyArIHBvb2xlZC5zdHJhdHVtICsgYWdlICsgc3RyYXR1bSwgZGF0YSA9IGluZmVydC5kYXRhW3RyYWluLCBdLAogICAgICAgICAgICAgc2l6ZSA9IDIsIHJhbmcgPSAwLjEsIGRlY2F5ID0gNWUtNCwgbWF4aXQgPSAyMDApCgpzdW1tYXJ5KG5uLmZpdCkKYGBgCgpubmV0IO2MqO2CpOyngOyXkOyEnCDsi6Dqsr3rp53snZgg7YyM652866+47YSw64qUIOyXlO2KuOuhnO2UvCDrmJDripQg7JeQ65+s7J2YIOygnOqzse2VqeydhCDqs6DroKTtlbQg7LWc7KCB7ZmUIOuQnOuLpC4g7Jes6riw7IScIOq4sOuzuOqwkuydgCDsl5Drn6zsnZgg7KCc6rOx7ZWpIOydtOuLpC4g7Lac66Cl6rKw6rO864qUIHNvZnRtYXjrpbwg7IKs7Jqp7ZW0IO2ZleuloOqzvCDqsJnsnYAg7ZiV7YOc66GcIOuzgO2ZmO2VoCDsiJgg7J6I6rOgIOqzvOygge2VqeydhCDrp4nquLDsnITtlbQg6rCA7KSR7LmYIOqwkOyGjCAod2VpZ2h0IGRlY2F5KeyYteyFmOydhCDsoJzqs7XtlZzri6QuIG5uZXQoKSDtlajsiJjripQg7Zmc7ISx7ZWo7IiY66GcIOyLnOq3uOuqqOydtOuTnCDrmJDripQg7ISg7ZiVIOy2nOugpSjquLDrs7jqsJLsnYAg7Iuc6re466qo7J2065OcIO2VqOyImCwgbGluZW91dCA9IEZBTFNFKeydhCDsgqzsmqntlZzri6QuCgojIyMgMTIuIOuqqOuNuCDsoIHtlakg6rKw6rO8IOyLnOqwge2ZlO2VmOq4sAoKYGBge3J9CmxpYnJhcnkoZGV2dG9vbHMpCgojIGltcG9ydCB0aGUgcGxvdCBmdW5jdGlvbiBmb3Igbm5ldAoKc291cmNlX3VybCgnaHR0cHM6Ly9naXN0LmdpdGh1YnVzZXJjb250ZW50LmNvbS9mYXdkYTEyMy83NDcxMTM3L3Jhdy80NjZjMTQ3NGQwYTUwNWZmMDQ0NDEyNzAzNTE2YzM0ZjFhNDY4NGE1L25uZXRfcGxvdF91cGRhdGUucicpCgpwbG90Lm5uZXQobm4uZml0KQoKYGBgCgrsnIQg6re466a87JeQ7ISc7J2YIOyEoOydmCDqtbXquLDripQg7Jew6rKw7ISg7J2YIOqwgOykkey5mOyXkCDruYTroYDtlZzri6QuIAoKCiMjIyAxMy4g7YWM7Iqk7Yq4IOuNsOydtO2EsOuhnCDrqqjrjbgg7ISx64qlIO2PieqwgCDsiJjtlontlZjquLAgCgpgYGB7cn0Kbm4ucHJvYnM8LXByZWRpY3Qobm4uZml0LCBpbmZlcnQuZGF0YVt0ZXN0LCBdKQoKbm4ucHJlZDwtaWZlbHNlKG5uLnByb2JzID4gMC41LCAxLCAwKQp0YWJsZShZLnRlc3QsIG5uLnByZWQpCgojIEFjY3VyYWN5Cm1lYW4oWS50ZXN0ID09IG5uLnByZWQpCgpgYGAKCgojIyMgMTQuIFJPQyDqt7jrnpjtlITsmYAgQVVDIO2ZleyduO2VmOq4sCAKCmBgYHtyfQpsaWJyYXJ5KFJPQ1IpCnByPC1wcmVkaWN0aW9uKG5uLnByZWQsIFkudGVzdCkKcHJmPC1wZXJmb3JtYW5jZShwciwgbWVhc3VyZSA9ICd0cHInLCB4Lm1lYXN1cmUgPSAnZnByJykKcGxvdChwcmYsIG1haW4gPSAnUk9DIEN1cnZlJykKYXVjPC1wZXJmb3JtYW5jZShwciwgbWVhc3VyZSA9ICdhdWMnKQphdWM8LWF1Y0B5LnZhbHVlc1tbMV1dCmF1YwoKYGBgCgrrtoTshJ3qsrDqs7wgUk9DIOqzoeyEoOydtCDsmYTrp4ztlZjqsowg7Kad6rCA7ZWY6rOgLCBBVUMg6rCAIOyVvSAwLjYg7J2066+A66GcIOygge2VqeuQnCDrqqjrjbjsnbQg66ek7JqwIOyasOyImO2VnCDrqqjrjbjsnbTrnbzqs6DripQg7ZWgIOyImCDsl4bsp4Drp4wsIOyii+ydgCDruYTspojri4jsiqQg6rKw6rO866W8IOqwgOyguOyYrCDsiJjrj4Qg7J6I64ukLiAKCgojIyA2LjQuNiDsnbjqs7Xsi6Dqsr3rp50g67aE7ISdIOyYiOygnCB3aXRoIG5ldXJhbG5ldCDtlajsiJggCgrri6TsnYzsnYAgbmV1cmFsbmV0IO2MqO2CpOyngOydmCBuZXVyYWxuZXTtlajsiJjrpbwg7J207Jqp7ZW0IGluZmVydCDsnpDro4zroZwg7J246rO17Iug6rK966edIOu2hOyEneydhCDsiJjtlontlZjripQg7JiI7KCc64ukLiAKCgojIyMgMS4g7ZuI66Co7JqpIOuNsOydtO2EsOyFi+ydhCDsnbTsmqntlbQg7J246rO17Iug6rK966edIOuqqOuNuOydhCDsoIHtlantlZjquLAgCgpgYGB7cn0KbGlicmFyeShuZXVyYWxuZXQpCgpuZXQuZml0PC1uZXVyYWxuZXQoZm9ybXVsYSA9IFkgfiBzcG9udGFuZW91cyArIHBvb2xlZC5zdHJhdHVtICsgYWdlICsgc3RyYXR1bSwgZGF0YSA9IGluZmVydC5kYXRhW3RyYWluLCBdLCBoaWRkZW4gPSBjKDIsMiksIGVyci5mY3QgPSAnY2UnLCB0aHJlc2hvbGQgPSAwLjAxLCBsaW5lYXIub3V0cHV0ID0gRkFMU0UsIGxpa2VsaWhvb2QgPSBUUlVFLHN0ZXBtYXggPSAxZTcpICMgc3RlcG1heOulvCDsp4DsoJXtlZjsmIDsnYwgCmBgYAoKCgrsnYDri4nsuLXsnbQgMuqwnOyduCDrqqjrjbjsnYQg7KCB7Jqp7ZWY6riwIOychO2VtCDqsIHqsIEg7J2A64uJIOuFuOuTnOydmCDsiJjripQgMuqwnCwy6rCc66GcIO2VnOuLpC4g7J2066W8IOychO2VtCBuZXVyYWxuZXQoKSDtlajsiJjsnZggaGlkZGVuID0gYygyLDIpIOyYteyFmOydhCDsgqzsmqntlZzri6QuIHRocmVzaG9sZCDsmLXshZjsnYAg7Jik7LCo7ZWo7IiY7J2YIO2OuOuvuOu2hOyXkCDrjIDtlZwg6rCS7Jy866GcIOygleyngOq3nOy5mShzdG9wIHJ1bGUpIOycvOuhnCDsgqzsmqnrkJzri6QuIG5lYXVyYWxuZXQoKSDtlajsiJjripQg64uk7JaR7ZWcIOyXreyghO2MjChiYWNrLXByb3BhZ2F0aW9uKSDslYzqs6DrpqzsppjsnYQg7Ya17ZW0IOuqqOuNuOydhCDsg53shLHtlZzri6QuIAoKCiMjIyDsoIHtlanrkJwg66qo6424IOyLnOqwge2ZlO2VmOq4sAoKYGBge3J9CnBsb3QobmV0LmZpdCkKCmBgYAoKCuyLpO2WiSDqsrDqs7wg7J6F66Cl7Li17J20IDTqsJwsIOydgOuLiey4teydtCAy6rCcLCDstpzroKXsuLXsnbQgMeqwnOyduCDrqqjrjbjroZwg7KCB7ZWp65Cc6rKD7J2EIOuzvOyImCDsnojri6QuIAoKCiMjIyAzLiDsoIHtlanrkJwg66qo64247J2YIOy2lOqwgOyggeyduCDsoJXrs7Qg7ZmV7J247ZWY6riwIAoKYGBge3J9Cm5hbWVzKG5ldC5maXQpCgpgYGAKCiRkYXRhIDog7KCE7LK07J6Q66OMIAoKJGNvdmFyaWF0ZSwg7JmAIHJlc3BvbnNlIDog66qo6424IOygge2VqeyXkCDsgqzsmqnrkJwg7J6Q66OMCgokbmV0LnJlc3VsdDog7KCB7ZWp6rCSCgokc3RhcnR3ZWlnaHRzOiDqsIDspJHsuZjsnZgg7LSI6riw6rCSCgokd2VpZ2h0czog6rCA7KSR7LmY7J2YIOygge2VqeqwkiAKCiRyZXN1bHQubWF0cml4OiDqsrDqs7wg7ZaJ66Cs7JeQIOuMgO2VnCDsoJXrs7QgCgokZ2VuZXJhbGl6ZWQud2VpZ2h0czog7J2867CY7ZmUIOqwgOykkey5mCAKCgojIyMgNC4g66qo6424IOygge2VqeyXkCDsgqzsmqnrkJwg7J6Q66OM7JmAIOygge2VqeuQnCDqsJIg7ZmV7J247ZWY6riwIAoKYGBge3J9Cm91dDwtY2JpbmQobmV0LmZpdCRjb3ZhcmlhdGUsIG5ldC5maXQkbmV0LnJlc3VsdFtbMV1dKQpkaW1uYW1lcyhvdXQpPC1saXN0KE5VTEwsIGMoJ3Nwb3RhbmVvdXMnLCdwb29sZWQuc3RyYXR1bScsJ2FnZScsJ3N0cmF0dW0nLCdubi1vdXRwdXQnKSkKCmhlYWQob3V0KQpgYGAKCgojIyMgNS4g7J2867CY7ZmUIOqwgOykkey5mChnZW5lcmFsaXplZCB3ZWlnaHRzKeyXkCDrjIDtlZwg7Iuc6rCB7ZmU7ZWY6riwCgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLDIpKQpnd3Bsb3QobmV0LmZpdCwgc2VsZWN0ZWQuY292YXJpYXRlID0gJ3Nwb250YW5lb3VzJywgbWluID0gLTIuNSwgbWF4ID0gNSkKZ3dwbG90KG5ldC5maXQsIHNlbGVjdGVkLmNvdmFyaWF0ZSA9ICdwb29sZWQuc3RyYXR1bScsIG1pbiA9IC0yLjUsIG1heCA9IDUpCmd3cGxvdChuZXQuZml0LCBzZWxlY3RlZC5jb3ZhcmlhdGUgPSAnYWdlJywgbWluID0gLTIuNSwgbWF4ID0gNSkKZ3dwbG90KG5ldC5maXQsIHNlbGVjdGVkLmNvdmFyaWF0ZSA9ICdzdHJhdHVtJywgbWluID0gLTIuNSwgbWF4ID0gNSkKcGFyOW1mcm93ID0gYygxLDEpCgpgYGAKCuu2hOyEnSDqsrDqs7wg7J2867CY7ZmUIOqwgOykkey5mOydmCDrtoTtj6zroZzrtoDthLAgcG9vbGVkLnN0cmF0dW0sIGFnZSwgc3RyYXR1beydgCDrjIDrtoDrtoQg6rCS7J20IDAg6re87LKY7J2YIOqwkuydhCDqsIDsp4Drr4DroZwg7IKs66GALeuMgOyhsCDsg4Htg5zsl5Ag65Sw66W4IO2aqOqzvOqwgCDrr7jrr7jtlZjqs6Agc3BvbnRhbmVvdXPripQg7J2867CY7ZmUIOqwgOykkey5mOydmCDrtoTsgrDsnbQg7KCE67CY7KCB7Jy866GcIDHrs7Tri6Qg7YGs6riwIOuVjOusuOyXkCDruYTshKDtmJUg7Zqo6rO866W8IOqwgOynhOuLpOqzoCDtlaAg7IiYIOyeiOuLpC4gCgoK7J2867CY7ZmUIOqwgOykkey5mOuKlCDri6Trpbgg66qo65OgIOqzteuzgOufieyXkCDsnZjsobTtlZjrr4DroZwg6rCBIOyekOujjOygkOyXkOyEnCDqta3shozsoIHsnbgg6riw7JqU64+E66W8IOuCmO2DgOuCuOuLpC4K7JiI66W8IOuTpOyWtCAsIOuPmeydvOuzgOyImOqwgCDrqofrqocg6rSA7Lih6rCS7JeQIOuMgO2VtOyEnOuKlCDslpHsnZgg7JiB7Zal7J2EIOqwgOyngOupsCDri6Trpbgg6rSA7Lih6rCS7JeQIOuMgO2VtOyEnOuKlCDsnYzsnZgg7JiB7Zal7J2EIOqwgOynhOuLpOuptCDtj4nqt6DsoIHsnLzroZwgMOyXkCDqsIDquYzsmrQg7JiB7Zal7J2EIOqwluuKlCDqsoPsnbQg6rCA64ql7ZWY64ukLgoK66qo65OgIOyekOujjOyXkCDrjIDtlZwg7J2867CY7ZmUIOqwgOykkey5mOydmCDrtoTtj6zripQg7Yq57KCVIOqzteuzgOufieydmCDtmqjqs7zqsIAg7ISg7ZiV7J247KeA7J2YIOyXrOu2gOulvCDrgpjtg4Drgrjri6QuIArspoksIOyekeydgCDrtoTsgrDsnYAg7ISg7ZiVIO2aqOqzvOulvCDsoJzsi5ztlZjrqbAg7YGwIOu2hOyCsOydgCDqtIDsuKHqsJIg6rO16rCE7IOB7JeQ7IScIOuzgO2ZlOqwgCDsi6ztlZjri6TripQg6rKD7J2EIOuCmO2DgOuCtOuvgOuhnCDruYTshKDtmJXsoIHsnbgg7Zqo6rO86rCAIOyeiOydjOydhCDrgpjtg4Drgrjri6QgCgoKIyMjIDYuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7KCB7ZWp65CcIOuqqOuNuOydmCDribTrn7Ag7Lac66Ck6rCSIOqzhOyCsO2VmOq4sCAo7JiI7Lih6rCSIOq1rO2VmOq4sCkKCmBgYHtyfQp0ZXN0LmRhdGEub3V0PC1jb21wdXRlKG5ldC5maXQsIGluZmVydC5kYXRhW3Rlc3QsIF0pCmhlYWQodGVzdC5kYXRhLm91dCRuZXQucmVzdWx0KQoKIyBjb25mdXNpb24gbWF0cml4Cm5ldC5wcmVkPC1pZmVsc2UodGVzdC5kYXRhLm91dCRuZXQucmVzdWx0PjAuNSwgMSwgMCkKdGFibGUoaW5mZXJ0LmRhdGFbdGVzdCwgXSRZLCBuZXQucHJlZCkKCiMgQWNjdXJhY3kKbWVhbihuZXQucHJlZCA9PSBpbmZlcnQuZGF0YVt0ZXN0LCBdJFkpCmBgYAoKCgojIyA2LjUg7JWM7IOB67iUCgrslZnsg4HruJQg66qo6424OiDso7zslrTsp4Qg642w7J207YSw66Gc67aA7YSwIOyXrOufrCDqsJzsnZgg66qo64247J2EIO2VmeyKte2VnCDtm4Qg6rKw6rO866W8IOyihe2Vqe2VmOyXrCDslYzqs6DrpqzsppjsnZgg7JWI7KCV7ISx6rO8IOygle2ZleyEseydhCDrhpLsnbTripQg67Cp67KV7J2064ukLiAKCuuwsOq5heydgCDsnbzrsJjsoIHsnbgg66qo64247J2EIOunjOuTnOuKlOuNsCDsp5HspJHrkJjslrQg7J6I6rOgIOu2gOyKpO2MheydgCDslrTroKTsmrQg66y47KCc66W8IOunnuy2lOuKlOuNsCDstIjsoJDsnbQg66ee7LaU7Ja07KC4IOyeiOuLpC4KCuuwsOq5heydgCDqsIEg7IOY7ZSM7JeQ7IScIOuCmO2DgOuCnCDqsrDqs7zrpbwg7J287KKF7J2YIOykkeyVmeqwkuycvOuhnCDrp57stpTquLAg65WM66y47JeQIOqzvOygge2VqeydhCDtlLztlaDsiJgg7J6I64ukLgoK642w7J207YSwIOu2hO2VoOyLnCBjcmVhdGVEYXRhUGFydGl0aW9uIO2VqOyImOulvCDsgqzsmqntlZjrqbQg67CY7J2R67OA7IiY6rCS7J2YIOu5hOycqOydtCDsm5Drs7gg642w7J207YSw7JmAIOqwmeqyjCDsnKDsp4DrkJzri6QuCgphZGFiYWcg7Yyo7YKk7KeA7J2YIGJhZ2dpbmcg7ZWo7IiY64qUIOuwsOq5hSDrqqjrjbjsnYQg7IOd7ISx7ZWY6rOgIGJvb3N0aW5nIO2VqOyImOuKlCDrtoDsiqTtjIUg66qo64247J2EIOyDneyEse2VnOuLpC4KCuu2gOyKpO2MheydgCDsiJzssKjsoIHsnLzroZwg7ZWZ7Iq17Iuc7YKk6rOgIO2VmeyKte2VmOuKlCDqs7zsoJXsl5DshJwg7Jik64u17JeQIOuMgO2VtCDrhpLsnYAg6rCA7KSR7LmY66W8IOu2gOyXrO2VmOyngOunjCDsoJXri7Xsl5Ag64yA7ZW07ISc64qUIOuCruydgCDqsIDspJHsuZjrpbwg67aA7Jes7ZWY6riwIOuVjOusuOyXkCDsmKTri7Xsl5Ag642U7JqxIOynkeykke2VoCDsiJgg7J6I64ukLiAKCuuenOuNpCDtj6zroIjsiqTtirjripQg7J2Y7IKs6rKw7KCV64KY66y0IOu2hOyEneydmCDsmIjsuKEg7KCV7ZmV64+E66W8IOuGkuydtOq4sCDsnITtlbQg7ZWY64KY7J2YIOydmOyCrOqysOygleuCmOustOulvCDsgqzsmqntlZjripQg64yA7Iug7JeQIOuLpOyImOydmCDsnZjsgqzqsrDsoJXrgpjrrLTrpbwg7IKs7Jqp7ZW0IOqysOqzvOulvCDsmIjsuKHtlZjripQg7JWE7IOB67iUIO2VmeyKtSDquLDrspXsnbTri6QuIAoKCiMjIDYuNS4xIOyVmeyDgeu4lCDrqqjrjbjsnZgg6rCc64WQIAoK7JWZ7IOB67iUIOuqqOuNuOydgCDso7zslrTsp4Qg642w7J207YSw66Gc67aA7YSwIOyXrOufrCDqsJzsnZgg66qo64247J2EIO2VmeyKte2VnCDtm4Qg6rKw6rO866W8IOyihe2Vqe2VmOyXrCDslYzqs6DrpqzsppjsnZgg7JWI7KCV7ISx6rO8IOygle2ZleyEseydhCDrhpLsnbTripQg67Cp67KV7J2064ukLiDsnbTripQg642w7J207YSw7JeQ7IScIO2RnOuzuOy2lOy2nOuyleycvOuhnCDsl6zrn6ztm4jroKjsmqkg642w7J207YSwIOynke2VqeydhCDrp4zrk6TslrQg6rCB6rCB7J2YIOuNsOydtO2EsCDsp5Htlansl5DshJwg7ZWY64KY7J2YIOu2hOulmOq4sCjstZzsooUg66qo6424KSDrpbwg66eM65Ok7Ja0IOyVmeyDgeu4lO2VmOuKlCDrsKnrspXsnbTri6QuIAoK7JWE7IOB67iUIOq4sOuyleydgCDrhpLsnYAg7Y647ZalIChiaWFzKSDroZwg7J247ZWcIOqzvOyGjOygge2VqeqzvCDrhpLsnYAg67aE7IKwICh2YXJpYW5jZSkg7Jy866GcIOyduO2VnCDqs7zsoIHtlansnYQg7LWc7IaM7ZmU7ZWY64qU642wIOuPhOybgOydtCDrkJzri6QuCgrrsLDquYUoYm9vc3RyYXAgYWdncmVnYXRpbmcp7J2AIOydvOuwmOyggeyduCDrqqjrjbjsnYQg66eM65Oc64qU642wIOynkeykkeuQmOyWtOyeiOuLpOuptCwg67aA7Iqk7YyF7J2AIOyWtOugpOyatCDrrLjsoJzrpbwg66ee7LaU64qU642wIOy0iOygkOydtCDrp57stpTslrTsoLgg7J6I64ukLgoK67aA7Iqk7YyFKGJvb3N0aW5nKSAtIO2VmeyKte2VmOuKlCDqs7zsoJXsl5DshJwg7Jik64u17JeQIOuMgO2VtCDrhpLsnYAg6rCA7KSR7LmY66W8IOu2gOyXrO2VtOyEnCDsmKTri7XsnYQg7J6YIOunnuy2mCDrqqjrjbjsnYAg7LWc7KKFIOuqqOuNuOuhnCDshKDsoJXtlZzri6QuCgrrsLDquYXsnYAg67OR66Cs66GcIO2VmeyKtSwg67aA7Iqk7YyF7J2AIOyInOywqOyggeycvOuhnCDtlZnsirUuCgrrnpzrjaQg7Y+s66CI7Iqk7Yq4IC0g64yA7ZGc7KCB7J24IOuwsOq5hSDslYzqs6Drpqzsppgg66qo64247J2064ukLiDrnpzrjaQg7Y+s66CI7Iqk7Yq464qUIOydvOuwmOyggeycvOuhnCDshLHriqXsnbQg65uw7Ja064KY6rOgIOydmOyCrOqysOygleuCmOustCDsl6zrn6wg6rCc66W8IOyCrOyaqe2VtCDqs7zsoIHtlakg66y47KCc66W8IO2UvO2VoCDsiJgg7J6I64ukLiAKCgojIyMgNi41LjIKCuuwsOq5hShiYWdnaW5nL2Jvb3N0cmFwIGFnZ3JlZ2F0aW5nKSDslYzqs6Drpqzsppgg7JuQ66asIDog7KO87Ja07KeEIOyekOujjOyXkOyEnCDsl6zrn6wg6rCc7J2YIOu2k+yKpO2KuOueqSDsnpDro4zrpbwg7IOd7ISx7ZWY6rOgLCDqsIEg7ZGc67O47JeQIOuMgO2VtCDrtoTrpZjquLAoY2xhc3NpZmllcnMp66W8IOyDneyEse2VnCDtm4Qo66qo6424IO2VmeyKte2bhCkg6re4IOqysOqzvOulvCDqsrDtlakodm90aW5nL2F2ZXJhZ2UpIO2VmOyXrCDstZzsooUg66qo64247J2EIOunjOuTnOuKlCDrsKnrspXsnbTri6QuIOuwsOq5heydgCDqsIEg7IOY7ZSM7JeQ7IScIOuCmO2DgOuCnCDqsrDqs7zrpbwg7J287KKF7J2YIOykkeyVmeqwkuycvOuhnCDrp57stpTquLAg65WM66y47JeQIOqzvOygge2VqeydhCDtlLztlaAg7IiYIOyeiOuLpC4gCgrrsLDquYXslYzqs6Drpqzsppgg7JWM7JWE65Gs7JW8IO2VoOygkDog67Cw6rmF7J2AIOuzteybkCDstpTstpwg67Cp67KV7J2EIOyCrOyaqe2VmOq4sCDrlYzrrLjsl5Ag6rCZ7J2AIOuNsOydtO2EsOqwgCDtlZwg7ZGc67O47JeQIOyXrOufrCDrsogg7LaU7Lac65Cg7IiY64+EIOyeiOqzoCwg7Ja065akIOuNsOydtO2EsOuKlCDtlZzrsojrj4Qg7LaU7LacIOuQmOyngCDslYrsnYTsiJgg7J6I64ukLgoK64uk7J2A7J2AIGFkYWJhZyDtjKjtgqTsp4DsnZggYmFnZ2luZyDtlajsiJjroZwg7JWE7J2066as7IqkIOyekOujjOulvCDsnbTsmqntlbQg67Cw6rmFIOuqqOuNuOydhCDsoIHtlantlZjripQg7JiI7KCc64ukLiAKCmBgYHtyfQpsaWJyYXJ5KGFkYWJhZykKZGF0YSgiaXJpcyIpCnN1bW1hcnkoaXJpcykKYGBgCgppcmlzIOuKlCA16rCcIOuzgOyImOyZgCAxNTDqsJzsnZgg6rSA7Lih6rCS7J2EIOqwgOyngOqzoCDsnojri6QuIOq3uOykkSBTcGVjaWVzIOuKlCBzZXRvc2EsIHZlcnNpY29sb3IsIHZpcmdpbmljYSDshLgg6rCA7KeAIOuylOyjvOuhnCDrtoTrpZjrkJzri6QuIAoKCiMjIyAyLiBpcmlzIOyekOujjOulvCDtm4jroKjsmqkg642w7J207YSwIDgwJSDsmYAg7YWM7Iqk7Yq4IOuNsOydtO2EsCAyMCUg66GcIOu2hOumrO2VmOq4sCAKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpwYXJ0czwtY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHA9MC44KQoKIyB0cmFpbmluZyBkYXRhCmlyaXMudHJhaW48LWlyaXNbcGFydHMkUmVzYW1wbGUxLCBdCgojIHRlc3QgZGF0YQppcmlzLnRlc3Q8LWlyaXNbLXBhcnRzJFJlc2FtcGxlMSwgXQoKdGFibGUoaXJpcy50cmFpbiRTcGVjaWVzKQp0YWJsZShpcmlzLnRlc3QkU3BlY2llcykKYGBgCgrrjbDsnbTthLAg67aE7ZWgIOyLnCBjcmVhdGVEYXRhUGFydGl0aW9uIO2VqOyImOulvCDsgqzsmqntlZjsl6wgU3BlY2llcyDqsJLsnZgg67mE7Jyo7J20IOybkOuzuCDrjbDsnbTthLDsmYAg6rCZ6rKMIOycoOyngOuQmOuKlCDqsoPsnYQg7JWMIOyImCDsnojri6QuCgoKIyMjIDMuIO2biOugqOyaqSDrjbDsnbTthLDroZwgMTAw7ZqMIOuwmOuztSgxMDDqsJwg7Yq466asIOyImCDsgqzsmqkp7Jy866GcIOuwsOq5hSDrqqjrjbgg7KCB7ZWp7ZWY6riwCgpgYGB7cn0KYmFnLmZpdDwtYmFnZ2luZyhTcGVjaWVzIH4uLCBkYXRhID0gaXJpcy50cmFpbiwgbWZpbmFsID0gMTAwKQoKYGBgCm1maW5hbCA9IDEwMCDsnbjsnpDripQg7ZWZ7Iq17JeQIOyCrOyaqe2VoCDtirjrpqwg7IiYKOuwmOuzte2an+yImCnrpbwg7ISx7KCV7ZWc64ukLgoKCiMjIyDsoIHtlanrkJwg66qo64247J2YIOy2lOqwgOyggeyduCDsoJXrs7Qg7ZmV7J247ZWY6riwCgpgYGB7cn0KbHMoYmFnLmZpdCkKCmBgYAoKCgoKIyMjIDUuIOygge2VqeuQnCDrqqjrjbjsl5DshJwg7LKrIOuyiOynuCDtirjrpqwg7ZmV7J247ZWY6riwIAoKYGBge3J9CmJhZy5maXQkdHJlZXNbWzFdXQoKIyDssqsg67KI7Ke4IO2KuOumrCDsi5zqsIHtmZQgCnBsb3QoYmFnLmZpdCR0cmVlc1tbMV1dKQp0ZXh0KGJhZy5maXQkdHJlZXNbWzFdXSkKYGBgCgoK7LKrIOuyiOynuCDtirjrpqwg66qo642467aE7ISdIOqysOqzvCBQZXRhbC5MZW5ndGggPCAyLjYg7J246rK97JqwIHNldG9zYSDroZwg67aE66WY65CY6rOgLApwZXRhbC5MZW5ndGggPiAyLjYg7J206rOgIFBldGFsLldpZHRoIDwgMS43NSDsnbgg6rK97JqwIHZlcnNpY29sb3Ig66GcIOu2hOulmOuQmOqzoCwKcGV0YWwuTGVuZ3RoID4gMi42IOydtOqzoCBQZXRhbC5XaWR0aCA+PSAxLjc1IOyduCDqsr3smrAgdmVyZ2luaWNhIOuhnCDrtoTrpZjrkJzri6QuIAoKCiMjIyA2LiDshKTrqoXrs4DsiJgg7KSR7JqU64+EIO2ZleyduO2VmOq4sCAKCmBgYHtyfQpiYWcuZml0JGltcG9ydGFuY2UKCiMg7KSR7JqU64+E66W8IOyLnOqwge2ZlAoKYmFycGxvdChiYWcuZml0JGltcG9ydGFuY2Vbb3JkZXIoYmFnLmZpdCRpbXBvcnRhbmNlLCBkZWNyZWFzaW5nID0gVCldLCBtYWluID0gJ1ZhcmlhYmxlcyBSZWxhdGl2ZSBJbXBvcnRhbmNlJykKYGBgCgrrtoTshJ0g6rKw6rO8IFBldGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgg67OA7IiYIOyInOycvOuhnCDspJHsmpTrj4TqsIAg64aS7J2AIOqyg+ydhCDrs7wg7IiYIOyeiOuLpC4gCgoKIyMjIDcuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7Lih7J2EIOyImO2Wie2VmOqzoCwg67Cw6rmFIOuqqOuNuOydmCDshLHriqUg7Y+J6rCA7ZWY6riwIAoKYGBge3J9CmJhZy5wcmVkPC1wcmVkaWN0KGJhZy5maXQsIG5ld2RhdGEgPSBpcmlzLnRlc3QpCmJhZy50YjwtdGFibGUoYmFnLnByZWQkY2xhc3MsIGlyaXMudGVzdCRTcGVjaWVzKQpiYWcudGIKYGBgCgrrtoTshJ0g6rKw6rO8IOygleyYpOu2hOulmO2RnOulvCDrs7TrqbQgc2V0b3Nh64qUIDEw6rCcIOuqqOuRkCwgdmVyc2ljb2xvcuuKlCAxMOqwnOykkSA56rCcLCB2aXJnaW5pY2HripQgMTDqsJwg66qo65GQIOygnOuMgOuhnCDrtoTrpZjrkJwg6rKDIOydhCDrs7zsiJgg7J6I64ukLiAKCgojIyMg7KCV67aE66WY7JyoIChBY2N1cmFjeSkg6rO8IOyYpOulmOu2hOycqChFcnJvciByYXRlKSDqs4TsgrDtlZjquLAKCmBgYHtyfQptZWFuKGlyaXMudGVzdCRTcGVjaWVzID09IGJhZy5wcmVkJGNsYXNzKQoKYmFnLnByZWQkZXJyb3IKYGBgCgrrtoTshJ0g6rKw6rO8IOygleu2hOulmOycqChBY2N1cmFjeSkg64qUIDAuOTYg7J206rOgIOyYpOu2hOulmOycqCAoZXJyb3IgcmF0ZSnsnYAgMC4wMyDsnbTri6QuIAoKCiMjIDYuNS4zIOu2gOyKpO2MheqzvCDrtoTshJ0g7JiI7KCcIHdpdGggYm9vc3Rpbmcg7ZWo7IiYIAoK67aA7Iqk7YyFIOyVjOqzoOumrOymmDog67aA7Iqk7YyF7J2AIOuwsOq5heqzvCDrj5nsnbztlZjqsowg67O17JuQIOyehOydmCDsg5jtlIzrp4HsnYQg7ZWY7KeA66eMIOqwgOykkey5mOulvCDrtoDsl6ztlZzri6TripQg7LCo7J207KCQ7J20IOyeiOuLpC4g67aA7Iqk7YyF7J2AIOyInOywqOyggeycvOuhnCDtlZnsirXsi5ztgqTqs6Ag7ZWZ7Iq17ZWY64qUIOqzvOygleyXkOyEnCDsmKTri7Xsl5Ag64yA7ZW0IOuGkuydgCDqsIDspJHsuZjrpbwg67aA7Jes7ZWY7KeA66eMLCDsoJXri7Xsl5Ag64yA7ZW0IOuCruydgCDqsIDspJHsuZjrpbwg67aA7Jes7ZWY6riwIOuVjOusuOyXkCDsmKTri7Xsl5Ag642U7JqxIOynkeykke2VoCDsiJgg7J6I64ukLiDrtoDsiqTtjIUg6riw67KV7J2AIOygle2ZleuPhOqwgCDrhpLqsowg64KY7Jik64qUIOuwmOuptCDsnbTsg4HqsJIob3V0bGllcinsl5Ag7Leo7JW97ZWY64ukLgrrtoDsiqTtjIUg66qo64247JeQ64qUIEFkYUJvb3N0LCBYR0Jvb3N0LCBHcmFkaWVudEJvb3N0IOuTseydtCDsnojri6QuIAoK7JWE64uk67aA7Iqk7Yq464qUIOydtOyghOydmCDrtoTrpZjquLDsl5Ag7J2Y7ZW0IOyemOuquyDrtoTrpZjrkJwg6rKD7J2EIOydtOyWtOyngOuKlCDslb3tlZwg7ZWZ7Iq16riw6rCAIOyImOygle2VtOykhOyImOyeiOuLpOuKlCDsoJDsl5DshJwg64uk7JaR7ZWcIOyDge2ZqeyXkCDsoIHsmqntlaAg7IiYIOyeiOuLpC4gCgrslYTri6TrtoDsiqTtirjripQg7J6h7J2M7J20IOunjuydgCDrjbDsnbTthLDsmYAg7J207IOB6rCS7JeQIOy3qOyVve2VmOyngOunjCDqs7zsoIHtlansl5Ag642cIOy3qOyVve2VmOuLpC4gCgrri6TsnYzsnYAgYWRhYmFnIOydmCBib29zdGluZyDtlajsiJjroZwg7JWE7J2066as7IqkIOyekOujjOulvCDsnbTsmqntlbQg67aA7Iqk7YyFIOuqqOuNuOydhCDsoIHtlantlZjripQg7JiI7KCc7J2064ukLgoKIyMjIDEuIGFkYWJhZyDtjKjtgqTsp4DsmYAgaXJpcyDrjbDsnbTthLAg67aI66Gc7Jik6riwIAoKYGBge3J9CmxpYnJhcnkoYWRhYmFnKQpkYXRhKCdpcmlzJykKYGBgCgoKIyMjIDIuIGlyaXMg7J6Q66OM66W8IO2biOugqOyaqSDrjbDsnbTthLAgODAlIOyZgCDthYzsiqTtirgg642w7J207YSwIDIwJSDroZwg67aE66as7ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCnBhcnRzPC1jcmVhdGVEYXRhUGFydGl0aW9uKGlyaXMkU3BlY2llcywgcD0wLjgpCmlyaXMudHJhaW48LWlyaXNbcGFydHMkUmVzYW1wbGUxLCBdCmlyaXMudGVzdDwtaXJpc1stcGFydHMkUmVzYW1wbGUxLF0KCnRhYmxlKGlyaXMudHJhaW4kU3BlY2llcykKdGFibGUoaXJpcy50ZXN0JFNwZWNpZXMpCgpgYGAKCiMjIyAzLiDtm4jroKjsmqkg642w7J207YSw66GcIDEwMCDtmozrsJjrs7UoMTAw6rCcIO2KuOumrCDsiJgg7IKs7JqpKeycvOuhnCDrtoDsiqTtjIUg66qo6424IOygge2Vqe2VmOq4sCAKCmBgYHtyfQpib28uZml0PC1ib29zdGluZyhTcGVjaWVzfi4sIGRhdGEgPSBpcmlzLnRyYWluLCBib29zID0gVCwgbWZpbmFsID0gMTAwKQoKYGBgCgojIyMgNC4g7KCB7ZWp65CcIOuqqOuNuOydmCDstpTqsIDsoIHsnbgg7KCV67O0IO2ZleyduO2VmOq4sCAKCmBgYHtyfQpscyhib28uZml0KQoKdGFibGUoYm9vLmZpdCRjbGFzcykKYGBgCmJvby5maXQg7JeQ7IScICRjbGFzcyDsho3shLEg7KCV67O07J2YIOu2hO2VoO2RnOyXkOyEnCBzZXRvc2HripQgNDAg6rCcLCB2ZXJzaWNvbG9y64qUIDQw6rCcLCB2ZXJnaW5pY2HripQgNDDqsJzroZwg67CY7J2R67OA7IiY6rCAIOu2hOulmOuQnCDqsoPsnYQg67O8IOyImCDsnojri6QuIAoKIyMjIDUuIOygge2VqeuQnCDrqqjrjbjsl5DshJwgMTAwIOuyiOynuCDtirjrpqwg7ZmV7J247ZWY6riwIAoKYGBge3J9CmJvby5maXQkdHJlZXNbWzEwMF1dCgojIDEwMCDrsojsp7gg66qo6424IOyLnOqwge2ZlCAKCnBsb3QoYm9vLmZpdCR0cmVlc1tbMTAwXV0pCnRleHQoYm9vLmZpdCR0cmVlc1tbMTAwXV0pCmBgYAoKCiMjIyA2LiDshKTrqoXrs4DsiJgg7KSR7JqU64+EIO2ZleyduO2VmOq4sCAKCmBgYHtyfQpib28uZml0JGltcG9ydGFuY2UKCiMg7KSR7JqU64+EIOyLnOqwge2ZlAoKYmFycGxvdChib28uZml0JGltcG9ydGFuY2Vbb3JkZXIoYm9vLmZpdCRpbXBvcnRhbmNlLCBkZWNyZWFzaW5nID0gVCldLCB5bGltID0gYygwLDEwMCksIG1haW4gPSAnVmFyaWFibGVzIFJlbGF0aXZlIEltcG9ydGFuY2UnKQpgYGAKCuu2hOyEnSDqsrDqs7wgcGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aCDqsIAg7KSR7JqU64+E6rCAIOuGkuydgOqyg+ydhCDrs7wg7IiYIOyeiOuLpC4gCgoKIyMjIDcuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7Lih7J2EIOyImO2Wie2VmOqzoCDrtoDsiqTtjIUg66qo64247J2YIOyEseuKpSDtj4nqsIDtlZjquLAgCgpgYGB7cn0KYm9vLnByZWQ8LXByZWRpY3QoYm9vLmZpdCwgbmV3ZGF0YSA9IGlyaXMudGVzdCkKYm9vLnRiPC0gdGFibGUoYm9vLnByZWQkY2xhc3MsIGlyaXMudGVzdCRTcGVjaWVzKQpib28udGIKCiMgQWNjdXJhY3kKbWVhbihpcmlzLnRlc3QkU3BlY2llcyA9PSBib28ucHJlZCRjbGFzcykKCiMgZXJyb3IgcmF0ZQpib28ucHJlZCRlcnJvcgpgYGAKCuu2hOyEnSDqsrDqs7wg7KCV7Jik67aE66WY7ZGc66W8IOuztOuptCDrqqjrkZAg7KCc64yA66GcIOu2hOulmOuQnOqyg+ydhCDrs7zsiJgg7J6I6rOgIOygleu2hOulmOycqChBY2N1cmFjeSnripQgMSDsnbTqs6Ag7Jik67aE66WY7JyoKGVycm9yIHJhdGUpIOydgCAwIOydtOuLpC4KCgojIyMgNi41LjQg656c642kIO2PrOugiOyKpO2KuOyZgCDrtoTshJ0g7JiI7KCcIAoK656c642k7Y+s66CI7Iqk7Yq4OiDsnZjsgqzqsrDsoJXrgpjrrLQg66qo64247J2YIOyYiOy4oSDsoJXtmZXrj4Trpbwg64aS7J206riwIOychO2VtCDtlZjrgpjsnZgg7J2Y7IKs6rKw7KCVIOuCmOustOulvCDsgqzsmqntlZjripQg64yA7Iug7JeQIOuLpOyImOydmCDsnZjsgqzqsrDsoJXrgpjrrLTrpbwg7IKs7Jqp7ZW0IOqysOqzvOulvCDsmIjsuKHtlZjripQg7JWZ7IOB67iUIO2VmeyKtSDquLDrspXsnbTri6QuCgrrnpzrjaTtj6zroIjsiqTtirgg7JWM6rOg66as7KaYOiDtm4jroKjsmqkg642w7J207YSw7IWL7JeQ7IScIOyehOydmOydmCDsg5jtlIzsnYQg67O17JuQIOy2lOy2nO2VmOyXrCDqsIEg7IOY7ZSM7JeQIOuMgO2VtOyEnOunjCDsnZjsgqzqsrDsoJXrgpjrrLTrpbwg66eM65Oc64qUIOuenOuNpCDtj6zroIjsiqTtirgg67Cp7Iud7J2AIOuwsOq5heqzvCDsnKDsgqztlZjri6QuCuq3uOufrOuCmCDrhbjrk5wg64K0IOuNsOydtO2EsOulvCDsnpDsi53rhbjrk5zroZwg64KY64iE64qUIOq4sOykgOydhCDsoJXtlaAg65WMLCDsoITssrQg7JiI7Lih67OA7IiY6rCAIOyVhOuLiOudvCDsmIjsuKHrs4DsiJjrpbwg7J6E7J2Y66GcIOy2lOy2nO2VmOyXrCDstpTstpzrkJwg67OA7IiY64K07JeQ7IScIOy1nOyggeydmCDrtoTtlaDsnYQg66eM65Ok7Ja0IOuCmOqwgOuKlCDrsKnrspXsnYQg7IKs7Jqp7ZWc64ukLgoK7JiI7Lih67Cp67KVOiDsg4jroZzsmrQg642w7J207YSw7JeQIOuMgO2VnCDsmIjsuKHsl5DshJwg66qp7ZGc67OA7IiY6rCAIOu2hOulmOydmCDqsr3smrDripQg64uk7IiY6rKwLCDtmozqt4DsnZgg6rK97Jqw7JeQ64qUIO2Pieq3oOydhCDst6jtlZjripQg67Cp67KV7J2EIOyCrOyaqe2VnOuLpC4gCgoK64uk7J2M7J2AIHJhbmRvbUZvcmVzdCDtlajsiJjroZwgc3RhZ2VjIOuNsOydtO2EsOyFi+ydhCDsnbTsmqntlZwg656c642k7Y+s66CI7Iqk7Yq4IOuqqOuNuOydhCDsoIHtlantlZjripQg7JiI7KCc64ukLgoKIyMjIDEuIHJhbmRvbUZvcmVzdCwgcnBhcnQg7Yyo7YKk7KeA7JmAIHN0YWdlYyDrjbDsnbTthLAg67aI65+s7Jik6riwIAoKYGBge3J9CmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHJwYXJ0KQpkYXRhKHN0YWdlYykKc3RyKHN0YWdlYykKdGFibGUoc3RhZ2VjJHBsb2lkeSkKYGBgCgrsi6Ttlokg6rKw6rO8IHN0YWdlYyDsnpDro4zripQg7LSdIDjqsJwg67OA7IiY7JmAIDE0NuqwnOydmCDqtIDsuKHqsJLsnbQg7KG07J6s7ZWY64qUIOqyg+ydhCDrs7wg7IiYIOyeiOuLpC4gCuuqqe2RnOuzgOyImOuKlCBkaXBsb2lkLCB0ZXRyYXBsb2lkLCBhbmV1cGxvaWQg7IS4IOqwgOyngCDrspTso7zroZwg67aE66WY65Cc64ukLiDrqqntkZzrs4DsiJjsnZgg67aE7ZWg7ZGc7JeQ7IScIGRpcGxvaWQgNjfqsJwsIHRldHJhcGxvaWQgNjjqsJwsIGFuZXVwbG9pZCAxMeqwnOyduCDqsoPsnYQg7JWMIOyImCDsnojri6QuIAoKCiMjIyAyLiDqsrDsuKHqsJIsIOykkeuzteuNsOydtO2EsCDtmZXsnbgg67CPIOygnOqxsCAKCgpgYGB7cn0KY29sU3Vtcyhpcy5uYShzdGFnZWMpKQpzdW0oaXMubmEoc3RhZ2VjKSkKCiMgTkEg7KCc6rGwCnN0YWdlYzI8LXN0YWdlY1tjb21wbGV0ZS5jYXNlcyhzdGFnZWMpLCBdCmNvbFN1bXMoaXMubmEoc3RhZ2VjMikpCgpucm93KHN0YWdlYzIpCgpzdW0oZHVwbGljYXRlZChzdGFnZWMyKSkKCmBgYArrtoTshJ0g6rKw6rO8IOqysOy4oeqwkuydtCAxMuqwnCDsnojslrTshJwg66qo65GQIOygnOqxsCDtlZjsmIDqs6Ag7KSR67O1642w7J207YSw64qUIOyhtOyerO2VmOyngCDslYrripTri6QuIAoKCiMjIyAzLiDrjbDsnbTthLDshYvsnYQg7ZuI66Co7JqpIOuNsOydtO2EsCA4MCXsmYAg7YWM7Iqk7Yq4IOuNsOydtO2EsCAyMCXroZwg67aE66as7ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCnBhcnRzPC1jcmVhdGVEYXRhUGFydGl0aW9uKHN0YWdlYzIkcGxvaWR5LCBwID0gMC44ICkKc3RhZ2VjLnRyYWluPC0gc3RhZ2VjMltwYXJ0cyRSZXNhbXBsZTEsIF0Kc3RhZ2VjLnRlc3Q8LSBzdGFnZWMyWy1wYXJ0cyRSZXNhbXBsZTEsIF0KCgp0YWJsZShzdGFnZWMudHJhaW4kcGxvaWR5KQp0YWJsZShzdGFnZWMudGVzdCRwbG9pZHkpCmBgYAoKCiMjIyA0LiDtm4jroKjsmqkg642w7J207YSw66GcIOuenOuNpCDtj6zroIjsiqTtirgg66qo64247J2EIOyDneyEse2VmOq4sApgYGB7cn0KcmYuZml0PC1yYW5kb21Gb3Jlc3QocGxvaWR5IH4uLCBkYXRhID0gc3RhZ2VjLnRyYWluLCBudHJlZSA9IDUwMCwgcHJveGltaXR5ID0gVCkKcmYuZml0CmBgYAoK7Iuk7ZaJIOqysOqzvCDsoJXsmKTrtoTrpZjtkZzsmYAg7Jik67aE66WY7Jyo7JeQIOuMgO2VnCBPT0Ig7LaU7KCV7LmY66W8IOygnOqzte2VnOuLpC4gCuuenOuNpCDtj6zroIjsiqTtirjripQg67aT7Iqk7Yq4656pIOyDmO2UjCDqs7zsoJXsl5DshJwg7KCc7Jm465CcIE9PQiDsnpDro4zrpbwg7IKs7Jqp7ZWY7JesIOqygOymneydhCDsi6Tsi5ztlaAg7IiYIOydtOyLvy4gCgojIyMgNS4gcGxvdCDtlajsiJjroZwg67CY7J2R67OA7IiYIOuylOyjvOuzhCDsoJXsmKTrtoTrpZjsnKgg7Iuc6rCB7ZmU7ZWY6riwIAoKYGBge3J9CnBsb3QocmYuZml0KQoKYGBgCgrqsoDsnYDsg4nsnbQg7KCE7LK0IOygleyYpOu2hOulmOycqCwg67mo6rCV7J20IGRpcGxvaWQsIOyXsOuRkOqwgCB0ZXRyYXBsb2lkLCDtlZjripjsnbQgYW5ldXBsb2lkIOydtOuLpC4KCgojIyMgNi4g7ISk66qF67OA7IiYIOykkeyalOuPhCDtmZXsnbjtlZjquLAgCgpgYGB7cn0KaW1wb3J0YW5jZShyZi5maXQpW29yZGVyKGltcG9ydGFuY2UocmYuZml0KSwgZGVjcmVhc2luZyA9IFQpXQoKIyDspJHsmpTrj4Qg7Iuc6rCB7ZmUIAp2YXJJbXBQbG90KHJmLmZpdCkKYGBgCgrrtoTshJ0g6rKw6rO8IGcyLCBwZ3RpbWUsIGFnZSwgZ2xlYXNvbiwgcGdzdGF0LCBncmFkZSwgZWV0IOuzgOyImCDsiJzsnLzroZwg7KSR7JqU64+E6rCAIOuGkuydgCDqsoPsnYQg67O8IOyImCDsnojri6QuIAoKCiMjIyA3LiDthYzsiqTtirgg642w7J207YSw66GcIOyYiOy4oeydhCDsiJjtlontlZjqs6AsIOuenOuNpCDtj6zroIjsiqTtirgg66qo64247J2YIOyEseuKpSDtj4nqsIDtlZjquLAgCgpgYGB7cn0KcmYucHJlZDwtcHJlZGljdChyZi5maXQsIG5ld2RhdGEgPSBzdGFnZWMudGVzdCkKZnIudGI8LXRhYmxlKHJmLnByZWQsIHN0YWdlYy50ZXN0JHBsb2lkeSkKZnIudGIKCiMgQWNjdXJhY3kKbWVhbihyZi5wcmVkID09IHN0YWdlYy50ZXN0JHBsb2lkeSkKCiMgRXJyb3IgcmF0ZQoKKDEtc3VtKGRpYWcoZnIudGIpL3N1bShmci50YikpKQoKYGBgCuu2hOyEnSDqsrDqs7wg7KCV7Jik67aE66WY7ZGc66W8IOuztOuptCBkaXBsb2lk64qUIDEz6rCcIOuqqOuRkCwgdGV0cmFwbG9pZOuKlCAxMuqwnCDrqqjrkZAsIGFuZXVwbG9pZOuKlCAx6rCcIOykkSAw6rCc6rCAIOygnOuMgOuhnCDrtoTrpZjrkJwg6rKD7J2EIOuzvCDsiJgg7J6I64ukLiDsoJXrtoTrpZjsnKgoQWNjdXJhY3kp64qUIOyVvSAwLjk2MiDsnbTqs6Ag7Jik67aE66WY7JyoKGVycm9yIHJhdGUp7J2AIOyVvSAwLjAzOOydtOuLpC4gCgoKIyMgNi42IOyEnO2PrO2KuCDrsqHthLAg66i47IugIAoK7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag66qo64247J2AIOuNsOydtO2EsOulvCDshKDtmJXsnLzroZwg67aE66as7ZWY64qUIOy1nOyggeydmCDshKDtmJUg6rKw7KCVIOqyveqzhOulvCDssL7ripQg7JWM6rOg66as7KaY7J2064ukLgoK66eI7KeE7J2AIOuRkCDrjbDsnbTthLAg6rWw6rO8IOqysOyglSDqsr3qs4TsmYAg65ao7Ja07KC4IOyeiOuKlCDsoJXrj4Trpbwg7J2Y66+47ZWc64ukLgoK7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6DsnYAg67aE66WY7JmAIOyImOy5mCDsmIjsuKEg66y47KCc7JeQIOuqqOuRkCDtmZzsmqntlaAg7IiYIOyeiOycvOupsCDrtoTrpZgg7ISx64ql7J20IOyasOyImO2VmOuptOyEnCDqs7zsoIHtlansnbQg7J6Y65CY7KeAIOyViuuKlOuLpC4KCuy7pOuEkO2KuOumreydgCDso7zslrTsp4Qg642w7J207YSw66W8IOyggeygiO2VnCDqs6DssKjsm5DsnLzroZwg7Jiu6ri0IOuSpCwg67OA7ZmY65CcIOywqOybkOyXkOyEnCDrjbDsnbTthLDrpbwg7J6YIOu2hOulmO2VoCDsiJgg7J6I64qUIOy0iO2PieuptOydhCDssL7ripTri6QuCgprc3ZtIO2VqOyImOuKlCDshJztj6ztirgg67Kh7YSwIOuouOyLoCDrqqjrjbjsnYQg7IOd7ISx7ZWc64ukLiAKCuygge2VqeuQnCDrqqjrjbjsl5DshJwg7LaU6rCA7KCB7J24IOygleuztOuKlCBscygp7ZWo7IiY66GcIO2ZleyduCDqsIDriqXtlZjri6QuIAoKY29zdOuKlCDslrzrp4jrgpgg66eO7J2AIOuNsOydtO2EsCDsg5jtlIzrk6TsnbQg64uk66W4IO2BtOuemOyKpOyXkCDrhpPsnbTripQg6rKD7J2EIO2XiOyaqe2VmOuKlOyngCDqsrDsoJXtlZzri6QuCgpnYW1tYeuKlCDtlZjrgpjsnZgg642w7J207YSwIOyDmO2UjOydtCDsmIHtlqXroKXsnYQg7ZaJ7IKs7ZWY64qUIOqxsOumrOulvCDqsrDsoJXtlZzri6QuIAoKCiMjIDYuNi4xIOyEnO2PrO2KuCDrsqHthLAg66i47IugIOuqqOuNuOydmCDqsJzrhZAKCuyEnO2PrO2KuCDrsqHthLAg66i47Iug64qUIOyEnOuhnCDri6Trpbgg67aE66WY7JeQIOyGje2VmOuKlCDrjbDsnbTthLAg6rCE7JeQIOqwhOqyqSjrp4jsp4QpIOydtOy1nOuMgOqwgCDrkJjripQg7ISgICjstIjtj4nrqbQpIOydhCDssL7slYTshJwg7J2066W8IOq4sOykgOycvOuhnCDrjbDsnbTthLDrpbwg67aE66WY7ZWY64qUIOuqqOuNuOydtOuLpC4g7KaJIFNWTeydgCDrjbDsnbTthLDrpbwg7ISg7ZiV7Jy866GcIOu2hOumrO2VmOuKlCDstZzsoIHsnZgg7ISg7ZiVIOqysOyglSDqsr3qs4Trpbwg7LC+64qUIOyVjOqzoOumrOymmOydtOuLpC4gCgrrp4jsp4TsnYAg65GQIOuNsOydtO2EsCDrspTso7zsmYAg6rKw7KCVIOqyveqzhOyZgCDrlqjslrTsoLgg7J6I64qUIOygleuPhOulvCDsnZjrr7jtlZzri6QuIAoK7ISc7Y+s7Yq4IOuyoe2EsCDslYzqs6Drpqzsppg6IOuRkCDrspTso7wg6rCE7J2YIOuNsOydtO2EsOulvCDqsJnsnYAg6rCE6rKp7Jy866GcIOq3uOumrOqzoCDstZzrjIDroZwg66mA66asIOuWqOyWtOynhCDshKAg65iQ64qUIO2PieuptOydhCDssL7ripTri6QuIOuRkOuylOyjvOqwhOydmCDrjbDsnbTthLDrpbwg64KY64iE64qUIOyngeyEoCDtmLnsnYAg7Y+J66m07J2AIOyXrOufrCDqsJzqsIAg7J6I7J2EIOyImCDsnojsp4Drp4wg7ZiE7J6s7J2YIO2biOugqOyaqSDrjbDsnbTthLDqsIAg7JWE64uMLCDrr7jrnpjsnZgg642w7J207YSw66W8IOu2hOulmCDsmIjsuKHtlZjripTrjbAg7LWc64yA7ZWcIOydvOuwmO2ZlO2VmOyXrCDrtoTrpZjrpbwg7J2064GM7Ja0IOuCvOyImCDsnojripQg7LWc64yA7Jes67CxIOy0iO2PieuptCDsnYQg7LC+6rOg7J6QIO2VmOuKlCDqsoPsnbTri6QuIOyXrOq4sOyEnCDsnbQg6rK96rOE7ISg6rO8IOqwgOyepSDqsIDquYzsmrQg6rCBIOu2hOulmOyXkCDsho3tlZwg7KCQ65Ok7J2EIOyEnO2PrO2KuCDrsqHthLAg65286rOgIO2VnOuLpC4gCgrrlYzroZzripQg6rOh7ISgIO2Yle2DnOuCmCDrjZQg67O17J6h7ZWcIO2Yle2DnOydmCDruYTshKDtmJUg67aE66WYIO2PieuptOycvOuhnCDrjbDsnbTthLDrpbwg67aE66WY7ZW07JW8IO2VoCDqsr3smrDrj4Qg7J6I64qU642wLCDsnbTrn7Dqsr3smrDsl5DripQg7Luk64SQ7Yq466at7J20652864qUIOq4sOuyleycvOuhnCDtlbTqsrDtlZzri6QuIAoK7Luk64SQ7Yq466atOiDso7zslrTsp4Qg642w7J207YSw66W8IOyggeygiO2VnCDqs6DssKjsm5DsnLzroZwg7Jiu6ri065KkLCDrs4DtmZjrkJwg7LCo7JuQ7JeQ7IScIOuNsOydtO2EsOulvCDsnpgg67aE66WY7ZWgIOyImCDsnojripQg7LSI7Y+J66m07J2EIOywvuuKlOuLpC4g7KaJIOy7pOuEkCDquLDrspXsnYAg7KO87Ja07KeEIOuNsOydtO2EsOulvCDqs6DssKjsm5Ag7Yq57KeVIOqzteqwhOycvOuhnCDsgqzsg4HtlbTso7zripQg6rKD7J2064ukLiDsu6TrhJAg6riw67KV7JeQ7IScIOuNsOydtO2EsOulvCDqs6DssKjsm5DsnLzroZwg67OA7ZmY7ZWY64qUIOuMgOyLoOyXkCDqs6DssKjsm5Ag67Kh7YSwIOqwhCDrgrTsoIEg6rOE7IKw7J2EIOyImO2Wie2WiOydhCDrlYzsmYAg7Jyg7IKs7ZWcIOqwkuydhCDrsJjtmZjtlZjripQg7Luk64SQIO2VqOyImOulvCDsgqzsmqntlZzri6QuIAoK64yA7ZGc7KCB7J24IOy7pOuEkCDtlajsiJg6IOuLpO2VrSDsu6TrhJAsIOqwgOyasOyLnOyViCDsu6TrhJAsIOugiOydtOuUlOyWvCDrsqDsnbTsiqQg7ZWo7IiYIOy7pOuEkCwg7Iuc6re466qo7J2065OcIOy7pOuEkCDrk7HsnbQg7J6I64qU642wIOydvOuwmOyggeycvOuhnCDqsIDsmrDsi5zslYgg7Luk64SQ7J2YIOyEseuKpeydtCDqsIDsnqUg7Jqw7IiY7ZW0IOqwgOyepeunjuydtCDsgqzsmqnrkJzri6QuIAoKKirshJztj6ztirjrsqHthLAg7J6l7KCQKiogCgrrspTso7zrtoTrpZjrgpgg7IiY7LmYIOyYiOy4oSDrrLjsoJzsl5Ag66qo65GQIO2ZnOyaqeydtCDqsIDriqXtlZjri6QuCgrrhbjsnbTspogg642w7J207YSw7JeQIOyYge2WpeydhCDrp47snbQg67Cb7KeAIOyViuqzoCwg6rO87KCB7ZWp7J20IOyemCDsnbzslrTrgpjsp4Ag7JWK64qU64ukLgoK7J2867CY7KCB7Jy866GcIOu2hOulmCDrrLjsoJzsl5DshJwg64uk66W4IOyVjOqzoOumrOymmOuztOuLpCDshLHriqXsnbQg64aS7J2AIOqyg+ycvOuhnCDslYzroKTsoLgg7J6I64ukLgoK67aE66WYIOqyveqzhOqwgCDrs7XsnqHtlZwg67mE7ISg7ZiVIOusuOygnOydvCDqsr3smrAg7YOAIOq4sOuylSDrjIDruYQg7ISx64ql7J20IOyasOyImO2VmOuLpC4KCioq7ISc7Y+s7Yq467Kh7YSwIOuLqOygkCoqCgrstZzsoIEg67aE66WY66W8IOychO2VtCDsu6TrhJAg7ZWo7IiY7JmAIOunpOqwnOuzgOyImCDrk7Hsl5Ag64yA7ZWcIOuwmOuzteyggeyduCDsobDtlakg7YWM7Iqk7Yq46rCAIO2VhOyalO2VmOuLpC4KCuyeheugpSDrjbDsnbTthLDqsIAg64yA65+J7J206rGw64KYIOuzgOyImOqwgCDrp47snYAg6rK97JqwIOyYpOuenCDtm4jroKjsi5zqsITsnbQg7ZWE7JqU7ZWY64ukLgoK67Cw6rK97J20IOuQmOuKlCDsnbTroaDqs7wg7JWM6rOg66as7KaYIOq1rO2YhOyLnCDtg4Ag6riw67KV7JeQIOu5hO2VmOyXrCDsg4HrjIDsoIHsnLzroZwg64Kc7ZW07ZWcIOuptOydtCDsnojri6QuCgrqsrDqs7wg7ZW07ISd7J2064KYIOyEpOuqhSDrk7Hsl5Ag7J6I7Ja0IOyWtOugpOybgOydtCDsnojri6QuIAoKCuyEnO2PrO2KuCDrsqHthLAg66qo64247J2EIOychO2VnCDtjKjtgqTsp4A6IGtlcm5sYWIsIGUxMDcxCgoKIyMgNi42LjIg7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag67aE7ISdIOyYiOygnCB3aXRoIGtzdm0g7ZWo7IiYIAoKIyMjIDEuIGtlcm5sYWIg7Yyo7YKk7KeA7JmAIGlyaXMg642w7J207YSwIOu2iOufrOyYpOq4sCAKYGBge3J9CmxpYnJhcnkoa2VybmxhYikKZGF0YShpcmlzKQpgYGAKCgojIyMgMi4gaXJpcyDsnpDro4zrpbwg7ZuI66Co7JqpIOuNsOydtO2EsCA4MCXsmYAg7YWM7Iqk7Yq4IOuNsOydtO2EsCAyMCXroZwg67aE66as7ZWY6riwIAoKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpwYXJ0czwtY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHAgPSAwLjgpCgojIHRyYWluaW5nIGRhdGEKaXJpcy50cmFpbjwtaXJpc1twYXJ0cyRSZXNhbXBsZTEsIF0KCiMgdGVzdCBkYXRhCmlyaXMudGVzdDwtaXJpc1stcGFydHMkUmVzYW1wbGUxLCBdCgp0YWJsZShpcmlzLnRyYWluJFNwZWNpZXMpCnRhYmxlKGlyaXMudGVzdCRTcGVjaWVzKQpgYGAKCgojIyMgMy4g7ZuI66Co7JqpIOuNsOydtO2EsOuhnCDshJztj6ztirgg67Kh7YSwIOuouOyLoCDrqqjrjbgg7IOd7ISx7ZWY6riwIAoKYGBge3J9CnN2bS5maXQ8LWtzdm0oU3BlY2llcyB+LiwgZGF0YSA9IGlyaXMudHJhaW4pCnN2bS5maXQKCgpgYGAK7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag66qo6424IOyDneyEseyXkCDsnbTsmqntlZwga3N2bSgpIO2VqOyImOuKlCDsgqzsmqntlaAg7Luk64SQ7JeQIOuMgO2VnCDsmLXshZjsnYQg7KeA7KCV7ZWY7KeAIOyViuuKlOqyveyasCDroIjsnbTrlJTslrwg67Kg7J207IqkIO2VqOyImCDsu6TrhJDsnYQg7J207Jqp7ZWc64ukLiAKCiMjIyA0LiDthYzsiqTtirgg642w7J207YSw66GcIOyYiOy4oeydhCDsiJjtlontlZjqs6AsIOyEnO2PrO2KuCDrsqHthLAg66i47IugIOuqqOuNuOydmCDshLHriqUg7Y+J6rCA7ZWY6riwIAoKYGBge3J9CnN2bS5wcmVkPC1wcmVkaWN0KHN2bS5maXQsIG5ld2RhdGEgPSBpcmlzLnRlc3QpCmhlYWQoc3ZtLnByZWQpCgpzdm0udGI8LXRhYmxlKHN2bS5wcmVkLCBpcmlzLnRlc3QkU3BlY2llcykKc3ZtLnRiCgoKIyBBY2N1cmFjeQptZWFuKHN2bS5wcmVkID09IGlyaXMudGVzdCRTcGVjaWVzKQoKIyBFcnJvciByYXRlCigxLXN1bShkaWFnKHN2bS50YikpL3N1bShzdm0udGIpKQpgYGAKCuu2hOyEnSDqsrDqs7wgdmVyc2ljb2xvcuydmCAxMCDspJEgOeqwnOuKlCDsoJzrjIDroZwg67aE66WYIOuQmOyXiOqzoCDrgpjrqLjsp4Agc2V0b3NhLCB2aXJnaW5pY2Eg64+EIOuqqOuRkCDsoJzrjIDroZwg67aE66WYIOuQmOyXiOuLpC4g7KCV67aE66WY7Jyo7J2AIDAuOTY3IOydtOqzoCDsmKTrtoTrpZjsnKjsnYAgMC4wMzMg7J2064ukLiAKCgoKIyMgNi42LjMg7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag67aE7ISdIOyYiOygnCB3aXRoIHN2bSgpIO2VqOyImCAKCuuLpOydjOydgCBlMTA3MSDtjKjtgqTsp4Agc3ZtIO2VqOyImOuhnCDslYTsnbTrpqzsiqQg7J6Q66OM66W8IOydtOyaqe2VtCDshJztj6ztirgg67Kh7YSwIOuouOyLoCDrqqjrjbjsnYQg7IOd7ISxIO2VmOuKlCDsmIjsoJzri6QuIAoKIyMjIDEuIO2biOugqOyaqSDrjbDsnbTthLDroZwg7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag66qo6424IOyDneyEse2VmOq4sCAKCmBgYHtyfQpsaWJyYXJ5KGUxMDcxKQpzdm0uZml0PC1zdm0oU3BlY2llcyB+LiwgZGF0YSA9IGlyaXMudHJhaW4pCnN2bS5maXQKYGBgCgoKIyMjIDIuIOygge2VqeuQnCDrqqjrjbgg7LaU6rCA7KCB7J24IOygleuztCDtmZXsnbjtlZjquLAgCgpgYGB7cn0KbHMoc3ZtLmZpdCkKCnN2bS5maXQkY29zdAoKc3ZtLmZpdCRnYW1tYQpgYGAKCioqU1ZNIOydmCDquLDrs7gg66ek6rCc67OA7IiY7J24IGNvc3QoQykg7JmAIGdhbW1hKioKCuuNsOydtO2EsOyXkCDsnbTsg4HqsJLsnbQg6rSA7Lih65CcIOuNsOydtO2EsOqwgCDsnojsnYTrlYwsIOydtOufsCDrjbDsnbTthLDrpbwg7JmE67OA7ZWY6rKMIOu2hOumrO2VtOuCtOuKlCDqsoPsnYAg7Ja066Ck7Jq06rKD7J2064ukLiDsnbTrpbwg7ZW06rKw7ZWY6riwIOychO2VmOyXrCDslb3qsITsnZgg7Jik66WY66W8IO2XiOyaqe2VmOqyjOuQmOuKlOuNsCDsnbQg7ZeI7Jqp7KCV64+EKOyWvOuniOuCmCDrjbDsnbTthLAg7IOY7ZSM7J20IOuLpOuluCDtgbTrnpjsiqTsl5Ag64aT7J2064qUIOqyg+ydhCDtl4jsmqntlZjripTsp4Ag6rKw7KCVKeulvCBjb3N065286rOgIO2VnOuLpC4g7Ja064qQIOygleuPhOydmCBjb3N066W8IO2XiOyaqe2VqChjb3N066W8IOuCruqyjCDrtIQp7Jy866Gc7IScIOuqqOuNuOydhCDsg4jroZzsmrQg642w7J207YSw7JeQIOygge2Vqe2VoOuVjCDsoovsnYAg7ISx64ql7J2EIOuztOydvOyImCDsnojsp4Drp4wgY29zdOulvCDrhpLqsowg67O066m0IO2KuOugiOydtOuLnSDrjbDsnbTthLDrpbwg7J6YIOu2hOulmO2VmOqyoOyngOunjCDsg4jroZzsmrQg642w7J207YSwIOygge2VqeyXkOyEnCDsoovsnYAg7ISx64ql7J2EIOuztOydvOq4sCDtnpjrk6Tri6QuIOymiSBjb3N0IOqwkuydtCDrhIjrrLQg7J6R7Jy866m0IOqzvOyEnOygge2VqeydtCDrkKAg6rCA64ql7ISx7J20IOy7pOyngOqzoCwgYyDqsJLsnbQg64SI66y0IOuGkuycvOuptCDqs7zsoIHtlansnbQg65CgIOqwgOuKpSDshLHsnbQg7Luk7KeA6rKMIOuQnOuLpC4gCgpnYW1tYeuKlCDtlZjrgpjsnZgg642w7J207YSwIOyDmO2UjOydtCDsmIHtlqXroKXsnYQg7ZaJ7IKs7ZWY64qUIOqxsOumrOulvCDqsrDsoJXtlZzri6QuZ2FtbWEg6rCS7J20IO2BtOyImOuhnSDtlZzrjbDsnbTthLAg7Y+s7J247YSw6rCAIOyYge2WpeugpeydhCDtlonsgqztlZjripQg6rGw66as64qUIOynp+OFheyVhOyngOuKlCDrsJjrqbQgZ2FtbWHqsJLsnbQg7J6R7J2E7IiY66GdIO2VnCDrjbDsnbTthLAg7Y+s7J247YSw6rCAIOyYge2WpeugpeydhCDtlonsgqztlZjripQg6rGw66as64qUIOy7pOynhOuLpC4gZ2FtbWEg6rCS7J20IOy7pOyniOyImOuhnSDqsrDsoJUg6rK96rOE6rCAIOygkOygkCDrjZQg6rWs67aI6rWs67aI7ZW07KeA64qU642wIOydtOuKlCDspoksIGdhbW1hIOunpOqwnOuzgOyImOuKlCDqsrDsoJUg6rK96rOE7J2YIOqzoeuloOydhCDsobDsoJXtlZzri6Tqs6Ag66eQ7ZWg7IiYIOyeiOuLpC4KZ2FtbWEg6rCS7J20IOuEiOustCDsnpHsnLzrqbQg6rO87IaM7KCB7ZWp65CgIOqwgOuKpeyEseydtCDtgazqs6AsIGdhbW1hIOqwkuydtCDrhIjrrLQg64aS7Jy866m0IOqzvOygge2VqeydmCDsnITtl5jsnbQg7J6I64ukLiAKCgoKIyMjIDMuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7Lih7J2EIOyImO2Wie2VmOqzoCwg7ISc7Y+s7Yq4IOuyoe2EsCDrqLjsi6Ag66qo64247J2YIOyEseuKpSDtj4nqsIDtlZjquLAKCmBgYHtyfQojIHByZWRpY3QgdGhlIHRlc3Qgc2V0cwpzdm0ucHJlZDwtcHJlZGljdChzdm0uZml0LCBpcmlzLnRlc3QpCgojIGNvbmZ1c2lvbiBtYXRyaXgKc3ZtLnRiPC10YWJsZShzdm0ucHJlZCwgaXJpcy50ZXN0JFNwZWNpZXMpCnN2bS50YgoKIyBBY2N1cmFjeQptZWFuKHN2bS5wcmVkID09IGlyaXMudGVzdCRTcGVjaWVzKQoKIyBFcnJvciBSYXRlCigxLXN1bShkaWFnKHN2bS50YikpL3N1bShzdm0udGIpKQpgYGAKCuu2hOyEneqysOqzvCB2aXJnaW5pY2Eg64qUIDEw6rCcIOykkSA46rCc66W8IOygnOuMgOuhnCDrtoTrpZjtlZjsmIDqs6Ag64KY66i47KeAIHNldG9zYSDsmYAgdmVyc2ljb2xvciDripQg66qo65GQIOygnOuMgOuhnCDrtoTrpZgg65CY7JeI64ukLiDsoJXrtoTrpZjsnKjsnYAgMC45MzMg7J206rOgIOyYpOu2hOulmOycqOydgCDslb0gMC4wNjYg7J2064ukLgoKCgoKIyMg64KY7J2067iMIOuyoOydtOymiCAKCuuCmOydtOu4jCDrsqDsnbTspogg66qo64247J2AIOuqqe2RnOuzgOyImOydmCAg67KU7KO866W8IO2VmeyKteyLnO2CpOq4sCDsnITtlbQg7Ya16rOE7ZWZ7J2YIOuyoOydtOymiCDtmZXrpaAg7LaU7KCV7JeQIOq4sOuwmOydhCDrkZQg7ZmV66WgIOuqqOuNuOyduCDrsqDsnbTspogg7KCV66asIOuYkOuKlCDrsqDsnbTspogg66Ow7J2EIOyCrOyaqe2VnOuLpC4gCgrrsqDsnbTspogg7KCV66as64qUIOuRkCDtmZXrpaDrs4DsiJjsnZgg7IKs7KCEIO2ZleuloOqzvCDsgqztm4Qg7ZmV66WgIOyCrOydtOydmCDqtIDqs4Trpbwg64KY7YOA64K064qUIOygleumrOuLpC4KCuuCmOydtOu4jCDrsqDsnbTspojsnZgg7ZmV66Wg7KCBIOy2lOuhoCDrsKnrspXsnYAg7Ja065akIOqwgOyEpOydmCDtmZXrpaDsnYQg7Y+J6rCA7ZWY6riwIOychO2VtCDsnoTsnZjsoIHsnLzroZwg7IKs7KCEIO2ZleuloOydhCDrqLzsoIAg7KCV7ZWY6rOgLCDqtIDssLDrkJwg642w7J207YSw66W8IOq4sOuwmOycvOuhnCDtlZjripQg6rCA64ql64+E66W8IOqzhOyCsO2VtOyEnCDsspjsnYzsl5Ag7ISk7KCV7ZWcIOyehOydmOyggSDtmZXrpaDsnYQg67O07KCV7ZWY64qUIOuwqeuyleydtOuLpC4g7J2065WMIOuyoOydtOymiCDsoJXrpqzripQg7J2065+s7ZWcIO2ZleuloOydhCDtlbTshJ3tlZjripTrjbAg7J6I7Ja0IO2VteyLrOyggeyduCDqsJzrhZDsnYQg7KCc6rO17ZWc64ukLiAKCm5haXZlQmF5ZXMg7ZWo7IiY64qUIOuCmOydtOu4jCDrsqDsnbTspogg66qo64247J2EIOyDneyEse2VnOuLpC4KCgojIyMgNi43LjEg64KY7J2067iMIOuyoOydtOymiCDrqqjrjbjsnZgg6rCc64WQCgrrgpjsnbTruIwg67Kg7J207KaIIOuqqOuNuOydgCDrqqntkZzrs4DsiJjsnZggIOuylOyjvOulvCDtlZnsirXsi5ztgqTquLAg7JyE7ZW0IO2GteqzhO2VmeydmCDrsqDsnbTspogg7ZmV66WgIOy2lOygleyXkCDquLDrsJjsnYQg65GUIO2ZleuloCDrqqjrjbjsnbgg67Kg7J207KaIIOygleumrCDrmJDripQg67Kg7J207KaIIOujsOydhCDsgqzsmqntlZzri6QuCgoqKuuCmOydtOu4jCDquLDrspUg7J6lLCDri6jsoJAqKgoKKuyepeygkDoqCgrqsJzrhZDsnbQg64uo7Iic7ZWY6rOgIOqzhOyCsOydtCDruaDrpbTri6QKCuqzoOywqOybkOydmCDrjbDsnbTthLDshYvsl5Ag7KCB7ZWp7ZWY64ukCgrrjbDsnbTthLDsl5Ag64W47J207KaIIOuwjyDqsrDsuKHqsJLsnbQg7Y+s7ZWo65CY7Ja0IOyeiOyWtOuPhCDsnpgg64+Z7J6R7ZWc64ukCgoKKuuLqOygkCoKIAog67KU7KO8IOu2hOulmCDrrLjsoJzsl5Ag7KCB7ZWp7ZWY7KeA66eMLCDsmIjsuKHrkJwg67KU7KO87J2YIO2ZleuloOqwkuydhCDtmZzsmqntlbTslbwg7ZWgIOqyveyasOyXkOuKlCDsoIHtlantlZjsp4Ag7JWK64ukLgogCiDrj4Xrpr3rs4DsiJjrk6TsnbQg67KU7KO8IO2Yle2DnOqwgCDslYTri4wg7IiY7LmYIO2Yle2DnOydvCDqsr3smrDsl5DripQg7KCV7ZmV7ISx7J20IOuWqOyWtOynhOuLpC4KIAog64+F66a967OA7IiY6rCAIOyEnOuhnCDrj4Xrpr3soIHsnbTqs6AsIOykkeyalOuPhOqwgCDqsJnri6TripQg6rCA7KCV7J20IOychOuwsOuQmOuKlCDqsr3smrDsl5Ag7Jik66WY6rCAIOuwnOyDne2VoCDsiJgg7J6I64ukLiAKIAogCiMjIDYuNy4yIOuCmOydtOu4jCDrsqDsnbTspogg67aE7ISdIHdpdGggbmFpdmVCYXllcyDtlajsiJggCgrri6TsnYzsnYAgZTEwNzEg7Yyo7YKk7KeA7J2YIG5haXZlQmF5ZXMg7ZWo7IiY66GcIG1sYmVuY2gg7Yyo7YKk7KeA7J2YIEhvdXNlVm90ZXM4NCDsnpDro4zrpbwg7J207Jqp7ZW0IOuCmOydtOu4jCDrsqDsnbTspogg66qo64247J2EIOyDneyEse2VmOuKlCDsmIjsoJzri6QuIAoKCiMjIyAxLiBlMTA3MSwgbWxiZW5jaCDtjKjtgqTsp4DsmYAgSG91c2VWb3RlODQg642w7J207YSwIOu2iOuhnOyYpOq4sCAKCmBgYHtyfQpsaWJyYXJ5KGUxMDcxKQpsaWJyYXJ5KG1sYmVuY2gpCgpkYXRhKEhvdXNlVm90ZXM4NCwgcGFja2FnZSA9ICdtbGJlbmNoJykKc3RyKEhvdXNlVm90ZXM4NCkKdGFibGUoSG91c2VWb3Rlczg0JENsYXNzKQpgYGAKCuyLpO2WiSDqsrDqs7wgSG91c2VWb3Rlczg0IOuNsOydtO2EsOuKlCAxN+qwnCDrs4DsiJjsmYAgNDM16rCc7J2YIOq0gOy4oeqwkuydtCDsobTsnqztlZjripQg6rKD7J2EIOuzvCDsiJgg7J6I64ukLiDrr7jqta3snZgg7ZWY7JuQ7J2Y7JuQIDQzNSDrqoUg7KSR7JeQIDI2N+uqheydtCDrr7zso7zri7nsnbTqs6AgMTY466qF7J20IOqzte2ZlOuLueydtOuLpC4gCgojIyMgSG91c2VWb3Rlczg0IOuNsOydtO2EsCDsmpTslb0KCmBgYHtyfQpzdW1tYXJ5KEhvdXNlVm90ZXM4NCkKCmNvbFN1bXMoaXMubmEoSG91c2VWb3Rlczg0KSkKCnN1bShpcy5uYShIb3VzZVZvdGVzODQpKQpgYGAKCuu2hOyEneqysOqzvCDrsJjsnZHrs4DsiJgv66qp7ZGc67OA7IiYIENsYXNzIOulvCDsoJzsmbjtlZwg64KY66i47KeAIOuzgOyImOuTpOydgCDrqqjrkZAg6rKw7Lih6rCS7J20IOyhtOyerO2VnOuLpOuKlCDqsoPsnYQg7JWMIOyImCDsnojri6QuCgojIyMgMy4gSG91c2VWb3Rlczg0IOyekOujjOulvCDtm4jroKjsmqkg642w7J207YSwIDgwJeyZgCDthYzsiqTtirgg642w7J207YSwIDIwJeuhnCDrtoTrpqztlZjquLAgCgpgYGB7cn0KbGlicmFyeShjYXJldCkKCnBhcnRzPC1jcmVhdGVEYXRhUGFydGl0aW9uKEhvdXNlVm90ZXM4NCRDbGFzcywgcD0wLjgpCmRhdGEudHJhaW48LUhvdXNlVm90ZXM4NFtwYXJ0cyRSZXNhbXBsZTEsIF0KZGF0YS50ZXN0PC1Ib3VzZVZvdGVzODRbLXBhcnRzJFJlc2FtcGxlMSwgXQoKdGFibGUoZGF0YS50cmFpbiRDbGFzcykKdGFibGUoZGF0YS50ZXN0JENsYXNzKQpgYGAKCgojIyMgNC4g7ZuI66Co7JqpIOuNsOydtO2EsOuhnCDrgpjsnbTruIwg67Kg7J207KaIIOuqqOuNuOydhCDsg53shLHtlZjquLAgCgpgYGB7cn0KbmFpLmZpdDwtbmFpdmVCYXllcyhDbGFzcyB+LiwgZGF0YSA9IGRhdGEudHJhaW4pCgpgYGAKCgojIyMgNS4g7YWM7Iqk7Yq4IOuNsOydtO2EsOuhnCDsmIjsuKHsnYQg7IiY7ZaJ7ZWY6rOgLCDrgpjsnbTruIwg67Kg7J207KaIIOuqqOuNuOydmCDshLHriqUg7Y+J6rCA7ZWY6riwIAoKYGBge3J9Cm5haS5wcmVkPC1wcmVkaWN0KG5haS5maXQsIG5ld2RhdGEgPSBkYXRhLnRlc3QsIHR5cGUgPSAnY2xhc3MnKQpuYWkudGI8LXRhYmxlKG5haS5wcmVkLCBkYXRhLnRlc3QkQ2xhc3MpCm5haS50YgoKIyBBY2N1cmFjeQptZWFuKG5haS5wcmVkID09IGRhdGEudGVzdCRDbGFzcykKCiMgRXJyb3IgcmF0ZQooMS1zdW0oZGlhZyhuYWkudGIpKS9zdW0obmFpLnRiKSkKYGBgCgrrtoTshJ3qsrDqs7wgZGVtb2NyYXTsnZggNTPqsJzspJEgNDXqsJzqsIAg7KCc64yA66GcIOu2hOulmOuQmOyXiOqzoCByZXB1YmxpY2Fu7J2YIDMz6rCc7KSRIDMw6rCc6rCAIOygnOuMgOuhnCDrtoTrpZjrkJjsl4jri6QuIOygleu2hOulmOycqOydgCDslb0gMC44NzIg7J206rOgIOyYpOu2hOulmOycqOydgCDslb0gMC4xMjgg7J2064ukLgoKCiMjIDYuOCBrLey1nOq3vOygkSDsnbTsm4MgCgoKay3stZzqt7zsoJEg7J207JuDIOuqqOuNuOydgCDrqqntkZzrs4DsiJjsnZgg67KU7KO866W8IOyVjOyngCDrqrvtlZjripQg642w7J207YSwIOyFi+ydmCDrtoTrpZjrpbwg7JyE7ZW07ZW064u5IOuNsOydtO2EsOyFi+qzvCDqsIDsnqUg7Jyg7IKs7ZWcIGvqsJzsnZgg7KO867OAIOuNsOydtO2EsOyFi+ydhCDsiJjsp5HtlZjqs6AsIGvqsJzsnZgg642w7J207YSw7IWL7J20IOqwgOyepSDrp47snbQg7IaN7ZW0IOyeiOuKlCDrspTso7zroZwg7KeA7KCV7ZWY64qUIOuwqeyLneycvOuhnCDrtoTrpZgg7JiI7Lih7J2EIO2VmOuKlCDquLDrspXsnbTri6QuIAoK642w7J207YSwIOqwhOydmCDsnKDsgqzshLHsnYQg7Lih7KCV7ZWY64qUIOuwqeyLneydgCDsnbzrsJjsoIHsnLzroZwg65GQIOygkOqwhOydmCDsnKDtgbTrpqzrk5wg6rGw66as7J2YIOyXreyImOulvCDsgqzsmqntlZjqsbDrgpgg7ZS87Ja07IqoIOyDgeq0gOqzhOyImOulvCDsnbTsmqntlZjsl6wg6rOE7IKw7ZWc64ukLiAKCmtubiDtlajsiJjripQgay3stZzqt7zsoJEg7J207JuDIOuqqOuNuOydhCDsg53shLHtlZzri6QuCgp0cmFpbi5ra25uKCkg7ZWo7IiY64qUIOy1nOyggeydmCBrIOqwkuydhCBrLWZvbGQg6rWQ7LCo6rKA7KadIOuwqeuyleycvOuhnCDssL7ripTri6QuIAoKCgojIyA2LjguMSBrLey1nOq3vOygkSDsnbTsm4Mg66qo64247J2YIOqwnOuFkCAKCmst7LWc6re87KCRIOydtOybgyDrqqjrjbjsnYAg66qp7ZGc67OA7IiY7J2YIOuylOyjvOulvCDslYzsp4Ag66q77ZWY64qUIOuNsOydtO2EsCDshYvsnZgg67aE66WY66W8IOychO2VtO2VtOuLuSDrjbDsnbTthLDshYvqs7wg6rCA7J6lIOycoOyCrO2VnCBr6rCc7J2YIOyjvOuzgCDrjbDsnbTthLDshYvsnYQg7IiY7KeR7ZWY6rOgLCBr6rCc7J2YIOuNsOydtO2EsOyFi+ydtCDqsIDsnqUg66eO7J20IOyGje2VtCDsnojripQg67KU7KO866GcIOyngOygle2VmOuKlCDrsKnsi53snLzroZwg67aE66WYIOyYiOy4oeydhCDtlZjripQg6riw67KV7J2064ukLiAKCu2VtOuLuSDrjbDsnbTthLAg7KCQ6rO8IOycoOyCrO2VnCBr6rCc7J2YIOyjvOuzgCDrjbDsnbTthLAg7KCQ7JeQ7IScIOuLpOyImOqysOydmCDsm5DsuZnsl5Ag65Sw6528IOyDiOuhnOyatCDrspTso7zrpbwg6rKw7KCV7ZWY64qUIOuwqeyLneydtCBrLey1nOq3vOygkSDsnbTsm4Mg6riw67KV7J2064ukIAoKay3stZzqt7zsoJEg7J207JuDIOq4sOuyleyXkOyEnCDsoIHsoIjtlZwga+qwkuydhCDsoJXtlZjripQg6rKD7J20IOunpOyasCDspJHsmpTtlZjri6QuIOyXrOufrOqwgOyngCBrIOqwkuydhCDshKTsoJXtlbTrs7TrqbTshJwg67CY67O17KCB7Jy866GcIO2FjOyKpO2KuO2VmOyXrCDstZzsoIHsnZgg67aE66WYIOyEseuKpeydhCDrs7TsnbTripQgayDqsJLsnLzroZwg7LWc7KKF7KCB7Jy866GcIOygle2VmOuptCDrkJzri6QuIOuLpOunjCBrIOqwkuydgCDqtIDsuKHqsJJeMC41IOuztOuLpOuKlCDsnpHsnYAg6rKD7J20IOyii+uLpOqzoCDslYzroKTsoLjsnojri6QuCgoKKiprLey1nOq3vOygkSDsnbTsm4Mg6riw67KV7J2YIOyepSzri6jsoJAg67mE6rWQKioKCirsnqXsoJAqCgrslYzqs6DrpqzsppjsnbQg7J207ZW07ZWY6riwIOyJveqzoCDsp4HqtIDsoIHsnbTri6QuCgrrjbDsnbTthLDshYvsnZgg7ZmV66Wg67aE7Y+sIOuTseyXkCDrjIDtlZwg6rCA7KCV7J20IO2VhOyalO2VmOyngCDslYrri6QuCgrsgqzsoIQg66qo6424IOyEseyglSDrsI8g66qo7IiYIOy2lOygleydtCDtlYTsmpTsl4bri6QuCgrtm4jroKjsi5zqsITsnbQg67mg66W064ukLgoKCirri6jsoJAqCgprIOqwkuyXkCDrjIDtlZwg66qF7ZmV7ZWcIOq4sOykgOydtCDsl4bslrQg7Iuc7ZaJ7LCp7Jik7KCBIOygkeq3vOydtCDtlYTsmpTtlZjri6QuCgrtirnsoJXtlZwg6rCA7ISk7J2064KYIOuqqOuNuCDsl4bsnbQg7KO87Ja07KeEIOuNsOydtO2EsOulvCDthrXtlbQg67KU7KO87J2YIOu2hOulmCDqsrDqs7zrp4wg7YyQ64uo7ZWo7Jy866GcIOu2hOyEneydhCDthrXtlZwg7Ya17LCw66Cl7J2EIOyWu+q4sCDslrTroLXri6QuIAoK7IOI66Gc7Jq0IOuNsOydtO2EsOqwgCDso7zslrTsp4gg65aE66eI64ukIOuqqOuToCDrjbDsnbTthLDsmYDsnZgg7Jyg7IKs64+E66W8IOqzhOyCsO2VtOyVvO2VqOycvOuhnCDqt7jrp4ztgbwg7Iuc6rCE7IaM7JqU6rCAIOunjuuLpC4g7J2065+wIO2KueyEsSDrlYzrrLjsl5Ag6rKM7Jy866W4IO2VmeyKteycvOuhnCDrtojrprDri6QuIAoK642w7J207YSw7IWL7J2YIOuqqOuToCDrjbDsnbTthLDrk6Tqs7wg6rGw66asIOqzhOyCsOydhCDsnITtlbQg66mU7J24IOuplOuqqOumrOyXkCDqsIDsoLjsmYDslbwg7ZWo7Jy866GcIOunjuydgCDrqZTrqqjrpqzqsIAg7ZWE7JqU7ZWY64ukIAoKCiMjIyA2LjguMiBrLey1nOq3vOygkSDsnbTsm4Mg67aE7ISdIOyYiOygnCB3aXRoIGtubiDtlajsiJggCgrri6TsnYzsnYAgY2xhc3Mg7Yyo7YKk7KeA7J2YIGtubiDtlajsiJjroZwg7JWE7J2066as7IqkIOyekOujjOulvCDsnbTsmqntlbQgay3stZzqt7zsoJEg7J207JuDIOyVjOqzoOumrOymmCDrqqjrjbjsnYQg7IOd7ISx7ZWY64qUIOyYiOygnOuLpC4gCgojIyMgMS4gY2xhc3Mg7Yyo7YKk7KeA7JmAIGlyaXMg642w7J207YSwIOu2iOufrOyYpOq4sCAKCmBgYHtyfQpsaWJyYXJ5KGNsYXNzKQpkYXRhKCdpcmlzJykKCmBgYAoKCiMjIyAyLiBpcmlzIOyekOujjOulvCDtm4jroKjsmqkg642w7J207YSwIDgwJeyZgCDtirjsiqTtirgg642w7J207YSwIDIwJeuhnCDrtoTrpqztlZjquLAgCgpgYGB7cn0KbGlicmFyeShjYXJldCkKCiMgcGFydGl0aW9uCnBhcnRzPC1jcmVhdGVEYXRhUGFydGl0aW9uKGlyaXMkU3BlY2llcywgcCA9IDAuOCkKCiMgdHJhaW5pbmcgZGF0YQpkYXRhLnRyYWluPC1pcmlzW3BhcnRzJFJlc2FtcGxlMSwgXQoKIyB0ZXN0IGRhdGEKZGF0YS50ZXN0PC1pcmlzWy1wYXJ0cyRSZXNhbXBsZTEsIF0KCnRhYmxlKGRhdGEudHJhaW4kU3BlY2llcykKdGFibGUoZGF0YS50ZXN0JFNwZWNpZXMpCgpgYGAKCiMjIyAzLiBrID0gMSDrtoDthLAgaz0gMTAg7IKs7J207J2YIOuylOychOyXkOyEnCDsoJXrtoTrpZjsnKgg6rOE7IKw7ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoZm9yZWFjaCkKCmtubi5rPC1jKDEsMiwzLDQsNSw2LDcsOCw5LDEwKQoKa25uX3Jlc3VsdDwtZm9yZWFjaChrID0ga25uLmssIC5jb21iaW5lID0gcmJpbmQpICVkbyUgewogIGtubi5wcmVkPC1rbm4oZGF0YS50cmFpblssMTo0XSwgZGF0YS50ZXN0WywxOjRdLAogICAgICAgICAgICAgICAgZGF0YS50cmFpbiRTcGVjaWVzLCBrID0gayAsIHByb2IgPSBUKQoKYWNjLnZhbDwtbWVhbihrbm4ucHJlZCA9PSBkYXRhLnRlc3QkU3BlY2llcykKCnJldHVybihkYXRhLmZyYW1lKGsgPSBrLCBhY2MgPSBhY2MudmFsKSkKfQoKa25uX3Jlc3VsdApgYGAKCuu2hOyEnSDqsrDqs7wgayA9IDMg7J24IOqyveyasCDsoJXrtoTrpZjsnKjsnbQgOTYuNjclIOydtOuvgOuhnCBrIOqwkuydhCAzIOycvOuhnCDqsrDsoJXtlZzri6QuIAoKCiMjIyA0LiDspIDruYTrkJwg642w7J207YSw7IWL7Jy866GcIGst7LWc6re87KCRIOydtOybgyDrqqjrjbgg7IOd7ISx7ZWY6riwIAoKYGBge3J9Cmtubl9tb2RlbDwta25uKGRhdGEudHJhaW5bLDE6NF0sIGRhdGEudGVzdFssMTo0XSwgZGF0YS50cmFpbiRTcGVjaWVzLCBrID0gMywgcHJvYiA9IFRSVUUpCgpgYGAKCgoKIyMjIDUuIO2FjOyKpO2KuCDrjbDsnbTthLDroZwg7JiI7Lih7J2EIOyImO2Wie2VmOqzoCwgay3stZzqt7zsoJEg7J207JuDIOuqqOuNuOydmCDshLHriqUg7Y+J6rCA7ZWY6riwIAoKYGBge3J9Cmtubi50YjwtdGFibGUoa25uLnByZWQsIGRhdGEudGVzdCRTcGVjaWVzKQprbm4udGIKCiMgQWNjdXJhY3kKbWVhbihrbm4ucHJlZCA9PSBkYXRhLnRlc3QkU3BlY2llcykKCiMgRXJyb3IgcmF0ZQooMS1zdW0oZGlhZyhrbm4udGIpKS9zdW0oa25uLnRiKSkKYGBgCgrrtoTshJ0g6rKw6rO8IOygleyYpOulmOu2hOulmO2RnCAoY29uZnVzaW9uIG1hdHJpeCnrpbwg67O066m0IHNldG9zYeuKlCAxMOqwnCwgdmVyc2ljb2xvcuuKlCAxMOykkSA56rCcLCB2aXJnaW5pY2HripQg66qo65GQIOygnOuMgOuhnCDrtoTrpZjrkJwg6rKD7J2EIOuzvCDsiJgg7J6I64ukLiDsoJXrtoTrpZjsnKjsnYAg7JW9IDAuOTY37J206rOgIOyYpOu2hOulmOycqOydgCDslb0gMC4wMzMK7J2064ukLiAKCgojIyMgNi44LjMgay3stZzqt7zsoJEg7J207JuDIOu2hOyEnSDsmIjsoJwga2tubiDtlajsiJggCgrri6TsnYzsnYAga2tubu2MqO2CpOyngOydmCBra25uIO2VqOyImOuhnCBCcmVhc3RDYW5jZXIg7J6Q66OM66W8IOydtOyaqe2VtCBrLey1nOq3vOygkSDsnbTsm4Mg66qo64247J2EIOyDneyEse2VmOuKlCDsmIjsoJzri6QuIAoKIyMjIDEuIGtrbm4g7Yyo7YKk7KeA7JmAIEJyZWFzdENhbmNlciDrjbDsnbTthLAg67aI65+s7Jik6riwIAoKYGBge3J9CmxpYnJhcnkoa2tubikKbGlicmFyeShtbGJlbmNoKQoKZGF0YSgiQnJlYXN0Q2FuY2VyIikKYGBgCgoKIyMjIDIuIOqysOy4oeqwkiDtmZXsnbgg67CPIOygnOqxsO2VmOq4sCAKCmBgYHtyfQojIOqwgSDsu6zrn7zsnZgg6rKw7Lih6rCSIApjb2xTdW1zKGlzLm5hKEJyZWFzdENhbmNlcikpCgojIO2GoO2DiCDqsrDsuKHqsJIgCnN1bShpcy5uYShCcmVhc3RDYW5jZXIpKQoKIyDqsrDsuKHqsJIg7KCc6rGwIApCcmVhc3RDYW5jZXIyPC1CcmVhc3RDYW5jZXJbY29tcGxldGUuY2FzZXMoQnJlYXN0Q2FuY2VyKSwgXQoKIyDspJHrs7XqsJIg7YOQ7IOJIApzdW0oZHVwbGljYXRlZChCcmVhc3RDYW5jZXIyKSkKCiMg7KSR67O16rCSIOygnOqxsCAKQnJlYXN0Q2FuY2VyMjwtQnJlYXN0Q2FuY2VyMlshZHVwbGljYXRlZChCcmVhc3RDYW5jZXIyKSxdCmBgYAoK67aE7ISdIOqysOqzvCAxNuqwnOydmCDqsrDsuKHsuZjsmYAgOOqwnOydmCDspJHrs7XqsJLsnbQg7KG07J6s7ZWY7JiA6rOgIOqysOy4oey5mOyZgCDspJHrs7XqsJLsnYQg7KCc6rGw7ZWY7JiA64ukLiAKCgojIyMgMy4gQnJlYXN0Q2FuY2VyIOuNsOydtO2EsOuhnCDtm4jroKjsmqkg642w7J207YSwIDgwJSwg7YWM7Iqk7Yq4IOuNsOydtO2EsCAyMCXroZwg67aE66as7ZWY6riwIAoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCgpwYXJ0czwtY3JlYXRlRGF0YVBhcnRpdGlvbihCcmVhc3RDYW5jZXIyJENsYXNzLCBwID0gMC44KQoKZGF0YS50cmFpbjwtQnJlYXN0Q2FuY2VyMltwYXJ0cyRSZXNhbXBsZTEsIF0KZGF0YS50ZXN0PC1CcmVhc3RDYW5jZXIyWy1wYXJ0cyRSZXNhbXBsZTEsIF0KCnRhYmxlKGRhdGEudHJhaW4kQ2xhc3MpCnRhYmxlKGRhdGEudGVzdCRDbGFzcykKYGBgCgojIyMgNC4g7ZuI66Co7JqpIOuNsOydtO2EsOyFi+ydhCDsnbTsmqntlbQg7LWc7KCB7J2YIGsg6rCSIO2ZleyduO2VmOq4sC1jcm9zcyB2YWxpZGF0aW9uIHRvIGdldCB0aGUgYmVzdCBrLgoKYGBge3J9Cmtubi50cjwtdHJhaW4ua2tubihDbGFzcyB+LiwgZGF0YS50cmFpblssLTFdLCBrbWF4ID0gMTAsIGRpc3RhbmNlID0gMSwga2VybmVsID0gJ3JlY3Rhbmd1bGFyJykKCiMgbWlzc2NsYXNzaWZpY2F0aW9uIGVycm9ycyh0aGUgbG93ZXIsIHRoZSBiZXR0ZXIpCmtubi50ciRNSVNDTEFTUwoKCmtubi50ciRiZXN0LnBhcmFtZXRlcnMKCgpgYGAKCuu2hOyEneqysOqzvCBrID0gOSDsnbjqsr3smrAg7Jik67aE66WYIOyXkOufrCAobWlzc2NsYXNzaWZpY2F0aW9uIGVycm9ycykg6rCS7J20IOyVvSAwLjAzM+ycvOuhnCDqsIDsnqUg64Ku6rKMIOuCmOyYqCDqsoPsnYQg67O8IOyImCDsnojri6QudHJhaW4ua2tubiDtlajsiJjripQgayDqsJLsnYQgayBmb2xkIOq1kOywqOqygOymnSDrsKnrspXsnLzroZwg7LC+64qU64ukLgoKCiMjIyA1LiDspIDruYTrkJwgayDsmYAg642w7J207YSw66W8IOydtOyaqe2VmOyXrCBrLey1nOq3vOygkSDsnbTsm4Mg66qo6424IOyDneyEse2VmOq4sC0gYnVpbGluZyBtb2RlbAoKCmBgYHtyfQojIElEIOulvCDsoJzsmbjtlZwg67OA7IiY65Ok7J2EIO2PrO2VqCAKa2tuLmZpdDwta2tubihDbGFzcyB+LiwgZGF0YS50cmFpblssLTFdLCBkYXRhLnRlc3RbLC0xXSwgaz05LCBkaXN0YW5jZSA9IDEsIGtlcm5lbCA9ICdyZWN0YW5ndWxhcicpCnN1bW1hcnkoa2tuLmZpdCkKYGBgCgojIyMgNi4g7KCB7ZWp65CcIOuqqOuNuOydmCDstpTqsIDsoIHsnbgg7KCV67O0IO2ZleyduO2VmOq4sCAKCmBgYHtyfQpscyhra24uZml0KQoKYGBgCgojIyMgNy4g7YWM7Iqk7Yq4IOuNsOydtO2EsOuhnCDsmIjsuKHsnYQg7IiY7ZaJ7ZWY6rOgLCBrLey1nOq3vOygkSDsnbTsm4Mg66qo64247J2YIOyEseuKpSDtj4nqsIDtlZjquLAgCgpgYGB7cn0Ka2tuLnRiPC10YWJsZShra24uZml0JGZpdHRlZC52YWx1ZXMsIGRhdGEudGVzdCRDbGFzcykKa2tuLnRiCgojIEFjY3VyYWN5CgptZWFuKGtrbi5maXQkZml0dGVkLnZhbHVlcyA9PSBkYXRhLnRlc3QkQ2xhc3MpCgojIEVycm9yIHJhdGUKCigxLXN1bShkaWFnKGtrbi50YikpL3N1bShra24udGIpKQoKYGBgCgrrtoTshJ0g6rKw6rO8IOygleyYpOu2hOulmO2RnOulvCDrs7TrqbQgYmVuaWduIOydmCA4N+qwnCDspJEgODfqsJzrpbwg66qo65GQIOygnOuMgOuhnCDrtoTrpZjtlZjsmIDqs6AgbWFsaWduYW5064qUIDQ36rCcIOykkSA0MuqwnOulvCDsoJzrjIDroZwg67aE66WY7ZWY7JiA64ukLiBra25u66qo64247J2YIOygleu2hOulmOycqOydgCDslb0gMC45NjIg7J206rOgIOyYpOu2hOulmOycqOydgCDslb0wLjAzNyDsnbTri6QuCg==