1주차 강의

R 시작부터 종료까지


## 작업 경로 확인
getwd()
setwd("C:/Users/chaeu/OneDrive/바탕 화면")
getwd()

## help 함수 : 데이터 셋, 객체의 설명 확인 가능!
help(hist)
?hist
help.search("his") #() 안의 함수와 관련된 모든 패키지의 목록 생성
??his

## 함수의 기능을 시범적으로 살펴볼 수 있음(예시제공)
example(mean)

## R의 계산기능(연산 기호)
3+4   #더하기
7-3   #빼기
3*4   #곱하기
8/4   #나누기
3^3   #거듭제곱 
3**3  #거듭제곱(^도 가능)
7%%4  #나머지
7%/%4 #몫

sqrt(9) #square root(제곱근)
sin(pi/2)
cos(pi)
abs(-11) #절대값
log(10) #자연로그
log(10, base=10) #상용로그
log(4,2)
factorial(5) #5!
exp(5)

## 객체 할당 
a <- 3; a  #상수
b = c(1,2,3); b  #벡터
d <<- mean(b); d  #함수
a == d  #동등(할당이랑 다름! 불린값 나오는 것)
15 -> e; e  #추천하지 않음

objects() # 현재 작업공간에서의 객체 나열


# 객체 명명 규칙
a <- 3; a
A  #대소문자 구분
stat.five <- c(1:5); stat.five  # 온점 이용 가능
stat.ten <- c(1:10); stat.ten
2stat <- 22   # 오류발생(숫자는 맨 앞에 이용 불가)
_stat <- "오류"  # 오류발생(_는 맨 앞에 이용 불가)

## 기본 데이터 유형
suchi <- 3^5; suchi
mode(suchi)  #수치형

nonli <- 5>10; nonli 
mode(nonli)  #논리형

munja <- "쌍따옴표"; munja
mode(munja)  #문자형

bokso <- 2+4i; bokso
mode(bokso)  #복소수형

## 특수 데이터 유형
# NA : 아직 모르는 값(not available), 할당하지 않은 값
# NULL : 값이 존재하지 않음
# NaN : 수학적으로 정의가 불가능한 수(not a number) eg) 0/0, log(-5)
# inf : 양의 무한대
# -inf : 음의 무한대
x <- c(0,7,8)
x/x   # NaN 1 1
1/x   # Inf 0.1428571 0.1250000

## 데이터 유형 검증 함수
is.numeric(suchi)  # 수치형 여부
is.complex(munja)  # 실수형 여부
is.integer(x) # 정수형 여부
is.logical(x) # 논리형 여부
is.complex(x) # 복소수형 여뷰
is.character(x) # 문자형 여부

is.na(x) # NA 여부(NaN도 True로 반환!) / 이 외에도 is.null(x), is.nan(x), is.finite(x), is.infinite(x), is.matrix(x)

yesNA <- c(2,3, NA, NULL,NaN); yesNA
is.na(yesNA) 
is.null(yesNA)
is.nan(yesNA)


## 기본 유틸리티
ls() # r에서 지금까지 만들어놓은 모든 객체들 보여줌

rm(a) # 지우고 싶은 객체 제거

rm(list=ls())  # 모든 객체 제거

벡터

# 벡터로 합치는 인자 자체가 벡터면, 벡터를 풀어서 하나의 합침
vec_1 <- c(1,2,3); vec_1
vec_2 <- c(4:6); vec_2
vec_3 <- c(rep(7,3)); vec_3 
c(vec_2,vec_1,vec_3)  # [1] 4 5 6 1 2 3 7 7 7


## 수열 생성하기 
1:7   # n:m  : n부터 m까지 1단위로 증가(or 감소. n>m인경우)하는 수열 생성 eg) [1] 1 2 3 4 5 6 7
seq(from=1, to=15, by=2)  # 1부터 15까지 2단위로 증가하는 수열  eg) [1]  1  3  5  7  9 11 13 15
seq(1, 15, 2) # 위와 같은 의미
seq(1, -1, by=-0.5)  # 음의 방향으로 갈 수 있음 eg) [1]  1.0  0.5  0.0 -0.5 -1.0
seq(1, 7, length=3)  # 길이가 3이 되게끔. eg) [1] 1 4 7

rep(2, times=5)  # 2를 다섯번 반복  eg) [1] 2 2 2 2 2
rep(c(1,2,3),3)  # 전체를 3번 반복(times=3),  1 2 3 1 2 3 1 2 3
rep(c(4,2,5), times=c(4,3,4))  # 4는 4번, 2는 3번, 5는 4번 반복.  eg) [1] 4 4 4 4 2 2 2 5 5 5 5
rep(1:4, each=2)  # 각각 2번씩 반복,  [1] 1 1 2 2 3 3 4 4
rep(1:4, times=2) # 전체를 2번 반복.  [1] 1 2 3 4 1 2 3 4
rep(c(3,2,5), length=5)  # 길이가 5가 되게 반복    eg) [1] 3 2 5 3 2


## 벡터 인덱싱 : 원소 선택하기
vec_in <- c(1,3,5,7,9,11,13,15); vec_in
vec_in <- seq(1, 15, 2); vec_in
vec_in[5]  # 5번째에 있는 원소 선택

vec_in[1:4] # 1~4번째 원소
vec_in[1:4,8]  #에러
vec_in[c(1:4,8)]  # 복수의 값을 선택하고 싶으면 벡터로 인덱싱!!  1~4번째, 8번째 원소

vec_in[-2]  # 2번째 원소 제외
vec_in[-(1:5)]  # 1~5번째 원소 제외
vec_in[-c(1:5,8)] # 1~5번째, 8번째 원소 제외

# 조건에 맞는 원소 -> 논리형 벡터 이용
vec_in[vec_in<10] # 10보다 작은 원소들

# 이름붙여진 원소 -> 이름(names) 이용
region_code <- c("02","031","055","063","064")  
names(region_code) <- c("Seoul","Gyeonggi","Gyeongnam","Jeonbuk","Jeju")  # 각 원소의 이름을 정의
region_code
region_code["Gyeonggi"]
region_code[2]  # 인덱스로 해도 상관없긴 함
region_code[c("Seoul","Jeju")]
region_code[c(1,5)]

# 연습
fruit <- c("apple","berry","cherry","grape","lemon","orange","peach")
fruit[c(1,3)][1]  # 1~3번째 원소 중 1번째 원소 "apple"
fruit[c(1,1,3,3,3,3)]  # "apple"  "apple"  "cherry" "cherry" "cherry" "cherry"


## 벡터 데이터 추가하기 
old <- c(2,4,6,8); old
old <- c(old,10); old  # 뒤에 원소 10 추가
old <- c(c(-2,0),old); old  # 앞에 원소 -2, 0 추가
old[10] <- 20 ;old  # 10번째 자리의 원소를 20으로 (원소가 없는 부분은 자동으로 NA처리 됨)


## 벡터 연산 : 같은 자리에 있는 원소끼리 연산 수행함. 
v <- c(11,12,13,14,15); v
w <- c( 1, 2, 3, 4, 5); w
v+w
v-w
v*w  # [1] 11 24 39 56 75
v/w
w^v

# 길이가 서로 다른 벡터를 연산한다면? -> 모자란 쪽의 벡터가 다시 첫번째 원소로 돌아가서 재활용함 
recycle.6 <- c(1:6)
recycle.3 <- c(1:3)
recycle.6+recycle.3  # [1] 2 4 6 5 7 9 <- 1+1 2+2 3+3 4+1 5+2 6+3 꼴로 되는 것


## 연습문제
# 1) 1~100까지의 범위 내에서 5의 간격을 갖는 값 생성
x=seq(from=1,to=100,by=5)
x
# 2) 위에서 생성한 자료를 역순으로 나타내기
x[length(x):1]
rev(x) # reverse

# 3) 1~4까지의 값을 각각 2번, 3번, 2번, 3번 반복하는 벡터 생성
x<-c(1:4)
rep(x,times=c(2,3,2,3))
rep(c(1:4),c(2,3,2,3))
c(rep(1,2), rep(2,3), rep(3,2), rep(4,3))

# 4) 1~1000까지의 범위 내에서 길이가 12가 되게 일정한 간격을 갖는 값들을 생성하시오
seq(from=1, to=1000, length=12)
seq(1, 1000, length=12)
seq(1, 1000, 12) # length를 지정 안해주면 default인 by 기능이 사용됨(12씩 증가)
rm(list=ls())

## 문자형 벡터에 적용하는 함수 

# nchar(x) : 문자열 x를 구성하는 문자의 개수
words_2 <- c("R", "Python", "SAS"); words_2
nchar(words_2) #  [1] 1 6 3

# paste(... , sep="", collapse="") : 문자열의 결합. collapse는 결과값이 여러개의 벡터일때 하나의 벡터로 합치는 역할
# paste0(...) : paste(... , sep="") 와 동일
paste(c('첫','두','세','네','다섯'), rep('번째', 5), sep='')  # [1] "첫번째"   "두번째"   "세번째"   "네번째"   "다섯번째"
paste(c('첫','두','세','네','다섯'), rep('번째', 5), sep='', collapse=', ')  # [1] "첫번째, 두번째, 세번째, 네번째, 다섯번째"

# substr(x,start,stop): 문자열의 일부분 선택
substr("StrawBerry",start=6,stop=10)  # [1] "Berry"
substr("StrawBerry",6 ,10)
berry <- c("BlueBerry","CranBerry"); berry
substr(berry,5,9)   # 각 요소의 다섯부터 아홉 번째 자리의 값만 뽑기 
substr(berry,c(1,5),c(4,9))   #  첫번째 요소는 1-4번째, 두번째 요소는 5-9번째 자리. [1] "Blue"  "Berry"

# strsplit(x, split) : split 기준으로 문자열 분리 
cafe <- c("hot,choco","ice,americano","hot,mocca","ice,latte"); cafe
strsplit(cafe,split=",")
mode(strsplit(cafe,split=",")) # 데이터 구조 : list
strsplit("gaonnuri",split="")  # [1] "g" "a" "o" "n" "n" "u" "r" "i"


# sub(old, new, x) : x문자열 중 첫번째 old만 new로 수정
# gsub(old, new, x) : 모든 old를 new 로 수정 (global sub)
happy_end <- "I hate you. You hate me"; happy_end
sub("hate","love",happy_end)
gsub("hate","love",happy_end)


## 벡터 원소 삽입 삭제 관련 함수 
# replace(x, index, values) :  x의 index에 해당하는 순번의 값을 values 값으로 변경 
one2three <- c("one","two","three"); one2three
one2three <- replace(one2three,3,3); one2three
one2three <- replace(one2three,1:2,c(1,2)); one2three 

# append(x, values, after) : x에 values를 after에 해당하는 순번 뒤에 넣음 
one2three <- append(one2three,"1.5",after=1); one2three
one2three <- append(one2three,seq(2.1,2.9,0.1),after=3); one2three

# sort(x, decreasing = F, ...) : x 데이터를 오름차순 정렬(decreasing = T 면 내림차순) 
num <- c(9,1,2,4,2,4,6,8,5,3,2,1,9,6,10,1,2); num
sort(num) # 오름차순 (알파벳순, 가나다순)
sort(num,decreasing=T)  # 내림차순 
# 한글과 숫자를 같이 쓰면 숫자를 문자로 취급 후 정렬

# order(x, decreasing=F, ...) : x 의 데이터를 오름차순으로 정렬하고 원래의 "위치"를 출력 
abc <- c("f","d","b","e","a","c"); abc
order(abc)  # [1] 5 3 6 2 4 1  인덱스를 반환함!! 
sort(abc)   # [1] "a" "b" "c" "d" "e" "f"


## 데이터 형태 변환 
as.numeric(c("1","2"))  #문자형 to 수치형 
as.numeric(c("a","b"))  #강제 변환에 의해 NA값 생성
as.numeric(c(TRUE,FALSE))  #논리형 to 수치형

as.character(c(1,-1))  #수치형 to 문자형 
as.character(c(T,F))  #논리형 to 문자형

as.logical(c(0,1,2,555,999,-111))  # 0(FALSE) 이외의 수는 다 TRUE
as.logical(c("TRUE","T","FALSE","F"))  #문자형 to 논리형
as.logical(c("yes","no","good","bad"))  # TRUE(T),FALSE(F) 이외의 문자는 NA

as.factor(c(1,2,2,3,3,3,4,4))  #수치형 to 요인
as.factor(c("A","A","B","C","B"))  #문자형 to 요인
# as.vector(), as.matrix(), as.data.frame() 도 존재 


## mode() vs class()
# mode() : 물리적인 자료형 (수치형, 문자형, 리스트, 함수 등)
# class() : 추상적인 자료형 (객체지향 관점, 해석이 다양함)
date <- as.Date("2019-03-18"); date  #날짜형으로 변환
mode(date)  #수치형
class(date)  #날짜형

mat <- matrix(c("a","b","c","d"),nrow=2); mat  #행렬
mat2 <- matrix(c(1,2,3,4),nrow=2); mat  #행렬
mode(mat)  #문자형
mode(mat2)  #수치형
class(mat)  #행렬
class(mat2)  #행렬

요인(Factor)

## factor() : 범주형 데이터에 사용! (벡터의 구성요소들이 반복되는 경우 요인을 사용하는 것이 저장하는데 효율적)
species <- c("animal","fish","bird","animal","fish","bird","animal","fish","bird"); species
species <- factor(species); species  #levels(범주)는 알파벳순
size <- c(60,20,4,76,30,2,70,25,5); size
levels(species)  # level 만 확인
nlevels(species)  # level 갯수 확인
tapply(size,species,mean) # 각 level 별 mean으로 반환 

species <- c("animal","fish","bird","animal","fish","bird","animal","fish","bird"); species
species <- factor(species, levels=c("fish", "bird", "animal")); species  #levels 순서 지정
levels(species)

## 그룹별 요약
# tapply(X, Index, FUN) 
# X : 계산 대상이 되는 벡터
# Index : 그룹을 나누기 위한 기준
# FUN : 적용할 함수 ex : mean, sum, length, sd

## 연습문제 
cat("\n-------------\n")
animal<-rep(c("dog", "cat", "rabbit"),c(3,2,4));animal
age<-c(10,14,6,7,9,4,6,2,8);age
animal<-factor(animal);animal
tapply(age, animal, sd)

행렬 (matrix)

rm(list=ls())

## 행렬 생성하기
# matrix(data, nrow=1, ncol=1, byrow=F, dimnaves=NULL) 
A <- matrix(c(1,2,3,4,5,6,7,8,9),nrow=3,ncol=3); A  # 열기준으로 채움(첫번째 열부터 ||| )
A <- matrix(c(1,2,3,4,5,6,7,8,9),nrow=3,ncol=3,byrow=T); A  # 행 기준으로 채움 (첫번째 행부터 차례로)
A <- matrix(c(1,2,3,4,5,6,7,8,9),nrow=3,ncol=3,byrow=T, dimnames = list(c("row1", "row2", "row3"), c("col1", "col2", "col3"))); A
class(A)  # [1] "matrix" "array" 

# array(data, dim=length(data), dimnames=NULL)
dat <- c(1,2,3,2,4,6,3,6,9,4,8,12,5,10,15); dat
D <- array(dat,dim=c(3,5)); D
D <- array(dat,c(3,5)); D 
# array 함수는 matrix 함수의 byrow와 같은 옵션이 없기 때문에 입력 벡터를 미리 적절히 순서를 바꿔서 넣어줘야 함(첫번째 행부터)
class(D)

# rbind() : 행으로 결합 (위 아래로)
# cbind() : 열로 결합(좌 우로)
r1 <- c(1:5); r1
r2 <- seq(2,10,2); r2
r3 <- rep(3,5); r3
rb <- rbind(r1,r2,r3); rb
class(rb)

c1 <- c(1,2,3); c1
c2 <- 5:7; c2
c3 <- rep(8,3); c3
c4 <- seq(9,14,length=3); c4
cb <- cbind(c1,c2,c3,c4); cb
class(cb)


## 행과 열의 개수를 세는 함수
# nrow(), ncol() : 행, 열의 개수
# dim() : 데이터의 행, 열, 층의 수


## 행렬에서 구성요소 선택하기 -> A[행, 열]
A <- matrix(c(1,2,3,4,5,6,7,8,9), nrow=3, byrow=T); A
A[2,3] # 2행 3열 요소
A[c(1,2),3]  # 1,2행의 3열에 있는 요소들
A[1,]  # 1행 모든 열
A[,2]  # 모든 행 2열
A[,3][2]  # 모든 행 3열의 2번째 원소


## 행렬의 기본연산
X <- matrix(c(4,8,12,16),nrow=2); X
Y <- matrix(c(3,6,9,10),nrow=2); Y
X+Y
X-Y
X/Y
X*Y  #원소끼리 곱
X^Y
X%%Y # 나머지 

X%*%Y  #행렬 곱
t(X) # 전치 행렬
solve(X)  # 역 행렬
det(X) # 행렬식
diag(X) # 대각행렬 (대각원소들 제외한 원소들은 전부 0으로)

rowSums(X, na.rm=T) # 행별 합 (같은 행에 있는 원소들 합한 결과), na.rm=T로 결측값 무시하고 연산(디폴트는 F)
colSums(X, na.rm=T) # 열별 합 (같은 열에 있는 원소들 합한 결과)
rowMeans(A, na.rm=T) # 행별 평균
colMeans(A, na.rm=T) # 열별 평균

B <- matrix(c(2,1,4,2), nrow=2);B
solve(B) # 행렬식의 값이 0이여서 역행렬 존재 X
E <- matrix(1:6, nrow=3);E
solve(E) # 정방행렬이 아니여서 역행렬 존재 X

identity <- rep(1,5)
diag(identity) # identity를 대각 원소로 갖는 대각 행렬 (단위 행렬)
value<-c(2,4,5,9)
diag(value)


## apply(X, margin, fun, ...)  : 행렬에 연산 적용! 
# margin : 함수를 어떤 방향으로 적용할지. 1(rows, 같은 행에서)  or 2(columns)
# fun : 적용할 함수. mean, var, ...
mat <- matrix(c(4,3,1,7,10,9,8,12,16),nrow=3); mat
apply(mat,1,sum)  #1: rows
apply(mat,2,sum)  #2: columns
apply(mat,1,function(x){x^2+1})  # 벡터를 출력할때 행에 결쳐서 함수를 실행해도 결과는 열로 결합해서 보여줌!(각 원소에 적용한 결과를 전치행렬 꼴로 보여주는것.. 귀찮게 시리.. )
apply(mat,2,function(x){x^2+1})


## 행, 열에 이름 붙이기
# rownames(행렬) = c(행이름), colnames(행렬) = c(열이름)
score <- matrix(c(5,7,3,4,2,9),nrow=3); score
rownames(score) <- c("DongHyun","HyunJi","YouKyung");score  #행 이름
colnames(score) <- c("midterm","final");score  #열 이름

# dimnames(행렬) = list(c(행이름), c(열이름)) 
mid <- matrix(c(6,7,3,2,3,4),nrow=3); mid
dimnames(mid) <- list(c("DongHyun","HyunJi","YouKyung"),c("statistics","mathmatics"))
mid

# 아니면 걍 만들때부터 이름 지어주기
fin <- matrix(c(8,4,3,2,5,9),nrow=3,dimnames=list("name"=c("DongHyun","HyunJi","YouKyung"),"subject"=c("statistics","mathmatics")))
fin


## 연습문제 
#1)
A <- matrix(c(2,6,4,8,7,3,1,5,9), byrow=T, nrow=3);A
#2)
solve(A)
A%*%solve(A)
solve(A)%*%A
#3)
apply(A,2,mean)
apply(A,2,var)

데이터 프레임

rm(list=ls())

# data.frame() : 데이터 프레임 생성
num <- c(1:5); num
cha <- c("가","나","다","라","마"); cha
logi <- c(T,F,T,TRUE,FALSE); logi
df_1 <- data.frame(num,cha,logi); df_1
str(df_1) # 데이터의 구조 확인
df_2 <- data.frame(num,cha,logi,alp=c("a","b","c","d","e")); df_2 # 이런식으로 그냥 변수 바로 추가할 수 있음 


## 행, 열 이름 설정 
# row.names 옵션 이용 
dfdf <- data.frame(col1=1:2, col2=3:4); dfdf
dfdf <- data.frame(col1=1:2, col2=3:4, row.names=c("row1", "row2")); dfdf
# rownames(df) = c("어쩌구", "저쩌구") 이런 식으로 바꿀수도 있음 

# names() : 열 이름 반환
names(dfdf)  
names(dfdf) <- c("new_col1", "new_col2") # 열이름 바꾸기 


## 구성요소 선택하기 (drop=F 옵션 넣으면 원래 데이터 프레임 형태대로 세로로 반환 됨 )
# 위치(좌표)와 인덱스 활용 
df_2[2,] # 2번째 행 
df_2[,2] # 2번째 열
df_2[2]  # 열좌표 -> df 형태로 반환 
df_2[[2]] # 인덱스  -> 벡터 형태로 반환 
df_2[c(1:3),]
df_2[,c(1,2)]
df_2$cha  # cha 라는 이름을 가진 열 -> 벡터 반환 
df_2$cha[2] # cha열의 2번째 행

df_2[-2] # 2번째 열 제외 
df_2[-c(1:3),] # 1-3 행 제외 
df_2[,-c(2,4)] 

df_2$alp <- NULL  # NULL을 이용한 변수 제거 (데이터프레임 열 제거)
df_2


# 변수 이름 활용 
# 1) 벡터로 반환 
df_2 <- data.frame(num,cha,logi,alp=c("a","b","c","d","e")); df_2

df_2[["cha"]]
df_2$cha
df_2[,"cha"]

# 2) 데이터 프레임으로 반환 (대관호 하나만!)
df_2["cha"]
df_2[c("cha","alp")]
df_2[,c("cha","alp")]


## 논리 연산자
# == 같다. != 다르다. >= , <= , &, |
# A %in% (c(2,4, 5))  : A는 2, 4, 5 중 하나인 것
# 5개 변수 : Sepal.Length(꽃받침 길이), Sepal.Width(꽂받침너비), Petal.Length(꽃잎길이), Petal.Width, Species

## iris 데이터 살펴보기  
head(iris) # 맨 앞 6행 출력
head(iris,10)
tail(iris) # 맨 뒤 6행 출력
tail(iris,10)
str(iris) # 데이터 구조 확인 

mean(iris$Sepal.Length)
sd(iris$Sepal.Length)
hist(iris$Sepal.Length) # 히스토그램 


## 데이터 프레임에서 조건에 맞는 구성요소 선택하기
# 1. which(조건) : 조건을 만족하는 원소들의 위치(행 위치)를 출력 
which(iris$Sepal.Length > 6)
iris$Sepal.Length[c(51,52,53,54)]  #확인
iris[which(iris$Sepal.Length > 6),"Species"]  # 꽃받침 길이가 6보다 큰 종 
iris[which(iris$Sepal.Length<=6 & iris$Sepal.Width==3),]  # 해당 조건을 만족하는 관측치 
iris[which(iris$Sepal.Length<=6 & iris$Sepal.Width==3),"Species"]  # 해당 조건을 만족하는 종 
iris[which(iris$Petal.Length>1.5 | iris$Petal.Width<0.3),"Species"]

# 2. subset(df, subset=(조건), select=열이름, drop=F) -> df 형태로 반환 
# 조건을 만족하는 select열(따로 지정 안하면 모든 열)의 값들. drop=F면 df로 출력 T면 벡터로 출력 
subset(iris,subset=(iris$Sepal.Length > 7), select=Species)
iris[which(iris$Sepal.Length > 7),c("Sepal.Length","Species")]

subset(iris,select=c("Petal.Length","Species"),subset=(iris$Petal.Length<=3.5 & iris$Species!="setosa"))
subset(iris,subset=(iris$Sepal.Length<5.5 & iris$Species %in% (c("versicolor","virginica"))))



## 연습문제 
#1) 
iris[which(iris$Petal.Length > 6),c("Sepal.Length","Species")]
#2) 
subset(iris,select=c("Sepal.Length","Species"),subset=(iris$Petal.Length > 6))
#3) 붓꽃 종별 꽃잎 길이의 평균을 구하시오 
tapply(iris$Petal.Length, iris$Species, mean)
# 4) 꽃받침 길이가 5이하이고, 붓꽃의 종이 setosa 인 꽆잎 길이의 평균을 구하시오 
mean(iris[which(iris$Sepal.Length<=5 & iris$Species=="setosa"),"Petal.Length"]) # 값들이 벡터 형태로 나오니까 mean()
apply(subset(iris,select="Petal.Length",subset=(iris$Sepal.Length<=5 & iris$Species=="setosa")),2,mean) # df 형태니까 apply
rm(list=ls())

## 데이터 프레임 변수 추가하기 
# 1) df$열이름
df_p <- data.frame(x1=c(1,2,3,4),x2=c(2,4,6,8)); df_p
x12 <- df_p$x1 + df_p$x2; x12
df_p$x3 <- df_p$x1 + df_p$x2; df_p
df_p$x22 <- df_p$x2 / df_p$x1; df_p

# 2) df[,열번호] 
df_p[,5] <- df_p[,1] + df_p[,3]
df_p  #변수명이 V5로 설정(자동으로~)

# 3) df_new = transform(df, 열이름=c())
df_p <- transform(df_p,x1=c(0,0,0,0));df_p  #변수 수정
df_p <- transform(df_p,x5=x2+x3,meanx12=(x1+x2)/2);df_p  #변수 추가

# df_p <- transform(df_p, meanx12=NULL);df_p # 변수 제거
# df_p$meanx12 <-NULL;df_p # 변수 제거


## 문자형 변수를 범주형 변수(factor, 요인)로 변환 
df_p$ten[df_p$V5<10] <- "down"
df_p$ten[df_p$V5>=10] <- "up"
class(df_p$ten) #문자형

df_p$ten <- factor(df_p$ten); df_p$ten #범주(factor)형으로 변환 
class(df_p$ten)
str(df_p)


## 데이터 프레임의 결합 
# 1) rbind(), cbind() : 위아래로 결합(변수명이 일치해야 함!!), 좌우로 결합 
rbind(df_r,df_o)
cbind(df_r,df_o)


# 2) merge(x, y, by = 기준변수, by.x=, by.y =, all=F, all.x=F, all.y=F)
merge(df_1, df_2, by = "name") #inner join. 교집합 
merge(df_1, df_2, by = "name", all=T) #outer join. 합집합

merge(df_1, df_2, by= "name", all.x=T) #left outer join. 첫번째 df 값 유지 
merge(df_1, df_2, by= "name", all.y=T) #right outer join
merge(df_1, df_2, by= "name", all.x=T, all.y=T) #outer join

merge(df_1, df_3, by.x= "name", by.y= "student", all=T) # 기준 변수 이름이 다를때! 각각 기준변수 이름 지정.
## 데이터셋 불러오기 : option 키 누르면 경로복사 메뉴 보임!! 
# txt 형식 : read.table("경로/file", header=F, sep="", ...)
# csv 형식 : read.csv("경로/file", header=F, sep=",", ...)
# header : 변수명 여부, na.strings : 결측값 NA 표시 
score1 <- read.table("/Users/jay/Desktop/data/score1.txt",sep=";",na.strings="9999") # 9999라는 값을 NA 처리
score2 <- read.csv("/Users/jay/Desktop/data/score2.csv")

## 내보내기 
# write.table(data, file="경로", sep=" ", quote=T, row.names=T, col.names=T, ...)
# write.csv(data, file="경로", ...)
colnames(score1) <- c("NAME","AGE","MIDTERM");score1
score <- merge(score1,score2,by=c("NAME","AGE"),all=T); score

write.table(score,"/Users/jay/Desktop/data/scoreq_t.txt",sep="_",quote=F) # quote=F : 각 값들을 따옴표로 묶지 말아라.
write.csv(score,"/Users/jay/Desktop/data/scorer_c.csv",row.names=F) # 행 이름을 파일에 포함할지 여부


## 연습문제 
#1
score3 <- read.table("/Users/jay/Desktop/data/score3.txt",sep="\t");score3
#2
score4 <- data.frame(NAME = c("JUY","KJY","KGH"), AGE = c(26,23,22), FINAL = c(45,24,83));score4
#3
colnames(score3) <- c("NAME","AGE","MIDTERM");score3
#4
score5 <- merge(score3,score4,by=c("NAME", "AGE"), all=T);score5
#5
write.csv(score5,"/Users/jay/Desktop/data/score5.csv",row.names=F)
rm(list=ls())

## 데이터 다운로드 
install.packages("ggplot2")
library("ggplot2")
library(ggplot2)

## Cars93 데이터 파악 
library("MASS") # R에 있는 기본 패키지로 install.packages 과정이 필요 없음
help(Cars93)
str(Cars93)  
head(Cars93)
tail(Cars93)
summary(Cars93) # 각 데이터에 대한 요약된 정보 출력 

## 결측값 제거하기 
# anyNA() NA값 유무, is.na() NA값 유무 -> 둘다 NA 뿐만아니라 NaN도 T로 반환! 
# na.omit() 결측값이 있는 관측치 제거(행 삭제)  
AA <- c(1,2,NA,NaN,45); AA
anyNA(AA)
is.na(AA)
A<-data.frame(AA); A
na.omit(A)

Car <- Cars93
nrow(Car) # 93
colSums(is.na(Car))
Car[which(is.na(Car$Luggage.room)),c("Model","Rear.seat.room","Luggage.room")] # luggage.room 에 결측치가 있는 관측치 확인 

Car <- na.omit(Car)
nrow(Car) # 82
colSums(is.na(Car)) # 결측치 있는지 확인 


## 데이터 프레임 내 변수 이용하기 
# eg) 표준화 
# 1) 데이터명$변수명 
z_p <- (Car$Price - mean(Car$Price))/sd(Car$Price)
z_p
mean(z_p) # 평균 0
sd(z_p) # 표준편차 1

# attatch(데이터명) : 데이터명$변수명 으로 매번 쓰기 귀찮을 때 쓰면 자동으로 붙여줌 
attach(Car)  
z_p1 <- (Price - mean(Price))/sd(Price)
detach(Car) # 다시 떼기 
z_p2 <- (Price - mean(Price))/sd(Price)  # 오류 

# 2) with(데이터명, 표현식)
z_p2 <- with(Car,(Price - mean(Price))/sd(Price)) # with(데이터, 표현식)
z_p2


## 데이터프레임에 함수 적용하기
# 1) aggregate(함수적용변수명~그룹분류할변수명, 데이터, 함수)
?aggregate # 데이터를 그룹별로 요약할 때 사용
aggregate(Price~Manufacturer,Car,mean)  # 제조사별 price 평균 
aggregate(EngineSize~Manufacturer+Type,Car,sum) # 제조사별, type별 엔진크기 합 

# 2) apply(x, margin, 함수) : 
# x : 배열 혹은 행렬
# margin : 1(rows, 같은 행끼리 함수 적용), 2(columns)
?apply
Car_p <-Car[c(2,4:6)]; Car_p 
apply(Car_p[2:4],1,sd)  # 같은 행, 하나의 관측치 별 2-4번째 변수값들의 표준편차 
apply(Car_p[2:4],2,mean)  # 같은 열(2-4번째 열), 하나의 변수 별 평균 

# 3) tapply(벡터, 집단분류할변수, 함수, ...) : table apply. 요인의 level에 따라 해당되는(같은 위치의) 벡터를 묶고 함수를 적용 
?tapply
tapply(Car$Price, Car$Manufacturer, mean) 


## 데이터 프레임 분리하기 
# split(x, f, drop=F, ...) : 요인(f)의 level에 따라 데이터프레임(혹은 벡터, x)을 분해함 -> list 반환  
?split
air_p <- split(Car$Price,Car$AirBags); air_p  
str(air_p)
mean(air_p[[1]])  #Driver&Passenger
sum(air_p[[2]])  #Driver only
sd(air_p[[3]])  #None

배열(array)

# array(data, dim=c(a1,a2,a3,...), dimnames=list(),...)
arr1 <- array(1:9); arr1
arr2 <- array(LETTERS,dim=c(2,13)); arr2
arr3 <- array(month.abb,dim=c(2,2,3)); arr3 # 3차원 
arr4 <- array(seq(2,32,2),dim=c(2,2,2,2)); arr4 # 4차원... 늘리는대로 계속 늘릴 수 있음

arr <- array(c(1:12),dim=c(3,2,2),dimnames=list("행"=paste("행",c(1:3)),"열"=paste("열",c(1:2)),"면"=paste("면",c(1:2))))

dimnames(arr3) <- list(paste0("행",c(1,2)),paste0("열",c(1,2)),paste0("면",c(1:3))) 
dim(arr3) # 차원 확인 

## 배열의 연산 : 같은 위치의 원소값 끼리 연산함(같은 크기의 배열 이어야 사칙연산 수행가능!)
arr.x+arr.y  #더하기
arr.x-arr.y  #빼기
arr.x*arr.y  #곱하기
arr.x/arr.y  #나누기
arr.x^arr.y  #n제곱
arr.x%%arr.y  #나머지

arr.x%*%arr.y # %*% : 행렬의 경우 -> 행렬곱 , 배열의 경우 *의 결과를 합한 것과 같음 
sum(arr.x*arr.y)


## 배열에서의 데이터 검색 -> 행렬과 비슷 
# arr[행,열,면(차원)]
arr.x <- array(1:18,dim=c(3,3,2)); arr.x
arr.x[1]  
arr.x[18] # 18번째 데이터 
arr.x[1, , ]
arr.x[ ,1, ]
arr.x[ , , 1] 
arr.x[ , , 2]

arr.x[1,1, ] # 모든 면의 1행1열의 값 
arr.x[2,2, ] 
arr.x[2, ,1] 
arr.x[ ,3,2]

arr.x[1,-1, ] # 1열을 제외한, 모든 면의 1행 
arr.x[-3, , 2]

mean(arr.x[1, ,1])  # 1면의 1행 데이터의 평균값 

## 연습문제
#(1) iris 데이터의 각 변수별 결측치가 있는지 확인하기
colSums(is.na(iris))

#(2) aggregate 함수를 사용해서 iris 데이터의 Species별 Sepal.Length의 평균과 총합 구하기
aggregate(Sepal.Length~Species, iris, mean)
aggregate(Sepal.Length~Species, iris, sum)

#(3) tapply 함수를 사용해서 iris 데이터의 Species별 Sepal.Length의 평균과 총합 구하기
## 방법 1
tapply(iris$Sepal.Length, iris$Species, mean)
tapply(iris$Sepal.Length, iris$Species, sum)

## 방법 2
attach(iris)
tapply(Sepal.Length, Species, mean)
tapply(Sepal.Length, Species, sum)

리스트(list)

rm(list=ls())

# list() 이용 
obj <- list() #빈 리스트 객체 생성
obj[[1]] <- "user1" # 첫 번째 리스트 객체 저장
obj[[2]] <- 20 # 두 번째 리스트 객체 저장
obj[[3]] <- F # 세 번째 리스트 객체 저장
obj[[4]] <- c(70, 80, 90, 100) # 네 번째 리스트 객체 저장
obj

# 리스트 생성 및 분해 
list.user <- list("user1", 23, "M", c(80,85,90,95)); list.user
unlist(list.user)  # 벡터값을 ㅗ분해 
list.user2 <- list(ID = "user1", age = 23, gender = "M", grade = c(80, 85, 90, 85)); list.user2

# 리스트 항목에 이름 적용 
list.user2 <- list(ID = "user1", age = 23, gender = "M", grade = c(80, 85, 90, 85)); list.user2
names(list.user2)
names(list.user2) <- c("USER", "AGE", "GENDER", "GRADE") # 이름 수정 

# 다양한 유형의 데이터를 하나의 리스트에 다 넣을 수 있음!! 
list.ex <- list("Hong", c(1:10), c(T,F))
all_list <- list(c(1:5), matrix(1:9, 3,3), list.ex); all_list

all_list <- list(c(1:5), matrix(1:9, 3,3), list.ex); all_list
all_list[[1]] <- NULL # 리스트 객체 제거 
all_list

## 리스트 객체 변경 
all_list <- list(c(1:5), matrix(1:9, 3,3), list.ex); all_list
all_list[[1]][5] <- 10   # 첫번째 항목의 5번째 값을 10으로 변경 
all_list[[2]] <- matrix(1:12,3,4)
all_list[[2]] <- all_list[[2]][-3,]  # 2번째 항목의 3번째 행을 제외한 모든 값 
all_list

function()

mypower02 <- function(x,y){
  print(x^y)
}
mypower02(3,2)
mypower02(3,3)

if문 for문 while문


######### if 문 ##########
# if(조건){
# 참일때 실행할 것
# }else{
# 거짓일때 실행할 것
# }

x<- -3
if (x < 0){
  print(-x)
} else{
  print(x)
}

myabs <- function(x){
  if (x < 0){
    print(-x)
  } else{
    print(x)}
}
myabs(-2);


# 조건이 3개 이상인 경우 if - else if - else
x<-10
if (x>15) {
  print("x가 15보다 크다")
} else if (x>5) {
  print("x가 5보다 크다")
} else {
  print("x가 5이하이다")
}

## ifelse(조건, 참일때 값, 거짓일때 값)
x <- 1:10
ifelse(x %% 2 == 0, "짝수", "홀수")



############# for 문 ##############
# for(변수 in 반복구간){ 반복할 식}
for(i in 1:5){
  print(i)
}

for (i in c(3,4,2,1)) {
  print(i)
}


mysum <- function(n) {
  a <- 0
  for (i in 1:n) {
    a <- a+i
  }
  print(a)
}

## break : 반복문 완전히 종료
x <- 1:10
for(i in x){
  if(i==5){
    break}  # 5가 나오면 종료
  print(i)}

## next : 현재 반복은 건너뛰고 다음으로~
x <- 1:10
for(i in x){
  if(i==5){
    next} # 5가 나오면 다음으로
  print(i)}


# eg) 구구단
n <- 1:9; m <- 1:9
for( i in m) {
  for( j in n){
    result <- i*j; print(paste(i,  "*", j, "=", result))}
}

# 2차원 행렬 구조로 구구단 
M_t <- matrix(0, nrow=9, ncol=9); M_t
for(i in 1:9){
  for(j in 1:9){
    M_t[i,j] <- i*j}}
print(M_t)



# for() 반복문을 통해 구구단 만들기
for (i in 1:9) {
  for (j in 1:9) {
    print(paste0(i,"X",j,"=",i*j))
  }
}



################## while 문 ####################
# while(조건){반복할식} : 조건이 참이면 계속 수행
x<-0
y<-0
while(y<=10){
  x<-x+y
  y<-y+1
  print(paste0("x=",x, " y=",y))
}
x;y


# 피보나치수열 
fib<-0
last_fib<-1
while(last_fib<300){
  fib<-c(fib,last_fib)
  last_fib<-fib[length(fib)-1]+fib[length(fib)]
}
fib



# 1에서 100까지의 합 구하기 
a <- 1; result <- 0
while(a<=100){
result <- result +a
a <- a+1}
print(paste("총합:", result))


# 구구단 2단 출력 
x<-2; y<-2
while(y<=10){
  result <- x*y
  print(paste(x, "*",y,"=",result))
  y <- y+1}


# 1~10 까지의 짝수에서 4만 제외한 수 출력 
x<-1; a<-1
while(a<=10) {
  x<- c(x, a)
  y<-x[x%%2==0]
  if(a==10) {print(y[y!=4])} 
  a<-a+1} 

데이터 시각화

1.고수준 그래픽스 함수

rm(list=ls())

## 산점도 : plot(x, y, type="p", main="제목", sub="부제목", xlab="x축 제목", ylab="y축 제목")
# 옵션들 
# type = "어쩌구"  => 점의 형식
# : p 점, l 선, b 점&선, c 앞의 것에서 점은 없는 것, o점플롯 선플롯 중첩, h 각점에서 x축까지의 수직선, s왼쪽값을 기초로 계단 모양으로 연결, S 오른쪽 값을 기초로 계단모양으로 연결, n 축만그리고 플롯 없 

# lty = "어쩌구" => 선의 형식 (type="l"인 경우 사용)
# blank 투명선, solid 실선, dashed 대시선, dotted 도트선, dotdash 도트와 대시선, longdash 긴 대시선, twodash 2개의 대시선 

# xlim=c(,), ylim=c(,) : x,y 좌표의 최솟값, 최댓값 지정 

# ann=F : 축의 제목 표시하지 않음 (xlab="", ylab=""와 같은 효과)

# axes=F : 축 제거 

# 점의 종류(색, 기호)
# col="black", col=1 이런식으로 색설정 가능, 차례로 검정 빨강 초록 파랑 연파랑 보라 노랑 회색
# pch=1  점의 모양 지정(0~25)
# cex=0  점의 크기 지정
# lwd=1  선의 굵기 지정(테두리)
# bg="blue" 점의 배경색 지정(pch=21~25 값에만 해당)

cars   # R의 내장 데이터인 cars 살펴보기
plot(cars$speed, cars$dist) # 기본 산점도 
plot(cars$speed, cars$dist, main="Graph of speed and distance") # 제목부여 
plot(cars$speed, cars$dist, main="Graph of speed and distance", xlab="speed", ylab="distance") # x,y 축 라벨 지정

# type 옵션 : 산점도 종류
x <- cars$speed
y <- cars$dist
par(mfrow=c(2,5)) # 2행 5열
plot(x, y, type="p")
plot(x, y, type="l")


# lty 옵션 : 선 종류
?BOD # 데이터 살펴보기
x <- BOD$Time
y <- BOD$demand
par(mfrow=c(2,3))
plot(x, y, type="l", lty=1) # 이렇게 숫자로 써도 똑같은 효과.
plot(x, y, type="l", lty=2)


# 점의 종류, 색
par(mfrow=c(2,3))
plot(x, y, col=2) 
plot(x, y, col='blue') # 테두리색 
plot(x, y, col=2, pch=0) # 모양
plot(x, y, col=2, pch=0, cex=2) # 크기
plot(x, y, col=2, pch=0, cex=2, lwd=2)  # 테두리 선 굵기 
plot(x, y, col=2, pch=21, cex=2, lwd=2, bg='blue') # 채우기 색. pch가 21~25인 경우가 bg가 적용


## pairs() : 산점도 행렬. 변수 쌍 간의 산점도들 몽땅 그리기 
pairs(iris[1:4])
pairs(iris[1:4], lower.panel=NULL) # 대각선 아래 패널 표시하지 않기
pairs(iris[1:4], upper.panel=NULL) # 대각선 위 패널 표시하지 않기
# labals : 변수들의 이름을 지정하는 옵션
## 막대그래프 : barplot(height=데이터, width=막대폭, space=막대간격, col=색, names.arg=막대라벨, horiz=T면막대를 수평으로) 
fruits_count <- c(4,3,5,8)
names(fruits_count) <- c("Apple","Tomato","Grape","Melon") ; fruits_count
barplot(height=fruits_count)
barplot(height=fruits_count, width=c(0.5,1,1.5,2))
barplot(height=fruits_count, space=2)

barplot(height=fruits_count, col=c("lightblue","mistyrose","lightcyan","lavender")) # 색지정 
barplot(height=c(4,3,5,8), names.arg=c("Apple","Tomato","Grape","Melon")) # 막대들 이름 지정 
barplot(height=fruits_count, horiz=TRUE) # 막대를 수평으로
barplot(height=fruits_count, horiz=F) # 막대를 수직으로 (디폴트)
## 히스토그램 : hist(x=데이터, breaks=막대구간or막대갯수or자동으로쪼개기, freq=T면세로축빈도 F면밀도, probability=T면세로축밀도 F면 빈도)
class_height <- c(173,163,174,168,175,178,166,178,173,185)
hist(x=class_height)
hist(x=class_height, breaks=c(160,170,180,190)) # 구간 정하기
hist(x=class_height, probability=TRUE)
hist(x=class_height, prob=TRUE)

##Page 24
x <- rnorm(1000);x   # 정규분포를 따르는 난수 1000개 추출
hist(x) # default는 빈도(frequency)
hist(x, prob=T)   # 밀도로 나타냄

# breaks 자동으로 구간 쪼개기 
x <- rnorm(10000)
par(mfrow=c(1,3))
hist(x, breaks="Sturges", main="Sturges") # 디폴트, 
hist(x, breaks="Scott", main="Scott")
hist(x, breaks="Freedman-Diaconis", main="Freedman-Diaconis")

# nclass 로도 구간 쪼갤 수 있음! 
x <- rnorm(100,3,2); x <- round(x,2);x
par(mfrow=c(1,3))
hist(x, nclass=10,main="nclass=10") 
hist(x, nclass=20,main="nclass=20")
hist(x, nclass=30,main="nclass=30")

# y축 없애기 
hist(x, axes=T, main="axes=T")
hist(x, axes=F, main="axes=F")



## 연습문제
## 1번 : R에 내장된 iris 데이터에서 산점도 행렬의 그려 변수들간의 상관관계 파악하기
head(iris)
str(iris) # Species 변수는 Factor, 나머지 변수들은 수치형 변수임을 확인
pairs(iris[1:4]) # 수치형 변수들만 갖고 산점도 행렬 그리기

## 2번 : R에 내장된 cars 데이터에서 x축을 dist, y축을 speed로 하는 산점도 그래프 그리기
plot(cars$dist, cars$speed, col='blue', xlab='distance', ylab='speed', main='plot of distance and speed')

## 3번 : 평균이 3인 포아송 분포에서 10개의 난수를 추출하여 막대그래프 그리기
x<-rpois(10,3);x
barplot(x)

## 4번 : 평균이 4이고 표쥰편차가 2인 정규분포에서 10000개의 난수를 추출하여 y축이 밀도가 되게 하는 히스토그램 그리기
y<-rnorm(10000, 4, 2);y
hist(y, frequency=F)
hist(y, freq=F)
hist(y, probability=T)
hist(y, prob=T)
runif(4) # 균일분포(0~1)
rnorm(n=3, mean=0, sd=1 )  # 정규분포
rpois(n=3, lambda = 3) # 포아송분포
sample(1:5, 3, replace=T, prob=c(0.1, 0.1, 0.1, 0.1, 0.6)) # 내가 가진 데이터에서 추출 . 
# sample(내가 가진 데이터, 개수, replace = 반복여부, prob = 확률설정)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyAx7KO87LCoIOqwleydmAoKIyMgUiDsi5zsnpHrtoDthLAg7KKF66OM6rmM7KeACgpgYGB7ciBS6riw67O4fQoKIyMg7J6R7JeFIOqyveuhnCDtmZXsnbgKZ2V0d2QoKQpzZXR3ZCgiQzovVXNlcnMvY2hhZXUvT25lRHJpdmUv67CU7YOVIO2ZlOuptCIpCmdldHdkKCkKCiMjIGhlbHAg7ZWo7IiYIDog642w7J207YSwIOyFiywg6rCd7LK07J2YIOyEpOuqhSDtmZXsnbgg6rCA64qlIQpoZWxwKGhpc3QpCj9oaXN0CmhlbHAuc2VhcmNoKCJoaXMiKSAjKCkg7JWI7J2YIO2VqOyImOyZgCDqtIDroKjrkJwg66qo65OgIO2MqO2CpOyngOydmCDrqqnroZ0g7IOd7ISxCj8/aGlzCgojIyDtlajsiJjsnZgg6riw64ql7J2EIOyLnOuylOyggeycvOuhnCDsgrTtjrTrs7wg7IiYIOyeiOydjCjsmIjsi5zsoJzqs7UpCmV4YW1wbGUobWVhbikKCiMjIFLsnZgg6rOE7IKw6riw64qlKOyXsOyCsCDquLDtmLgpCjMrNCAgICPrjZTtlZjquLAKNy0zICAgI+u5vOq4sAozKjQgICAj6rOx7ZWY6riwCjgvNCAgICPrgpjriITquLAKM14zICAgI+qxsOuTreygnOqzsSAKMyoqMyAgI+qxsOuTreygnOqzsShe64+EIOqwgOuKpSkKNyUlNCAgI+uCmOuouOyngAo3JS8lNCAj66qrCgpzcXJ0KDkpICNzcXVhcmUgcm9vdCjsoJzqs7Hqt7wpCnNpbihwaS8yKQpjb3MocGkpCmFicygtMTEpICPsoIjrjIDqsJIKbG9nKDEwKSAj7J6Q7Jew66Gc6re4CmxvZygxMCwgYmFzZT0xMCkgI+yDgeyaqeuhnOq3uApsb2coNCwyKQpmYWN0b3JpYWwoNSkgIzUhCmV4cCg1KQoKIyMg6rCd7LK0IO2VoOuLuSAKYSA8LSAzOyBhICAj7IOB7IiYCmIgPSBjKDEsMiwzKTsgYiAgI+uyoe2EsApkIDw8LSBtZWFuKGIpOyBkICAj7ZWo7IiYCmEgPT0gZCAgI+uPmeuTsSjtlaDri7nsnbTrnpEg64uk66aEISDrtojrprDqsJIg64KY7Jik64qUIOqygykKMTUgLT4gZTsgZSAgI+y2lOyynO2VmOyngCDslYrsnYwKCm9iamVjdHMoKSAjIO2YhOyerCDsnpHsl4Xqs7XqsITsl5DshJzsnZgg6rCd7LK0IOuCmOyXtAoKCiMg6rCd7LK0IOuqheuqhSDqt5zsuZkKYSA8LSAzOyBhCkEgICPrjIDshozrrLjsnpAg6rWs67aECnN0YXQuZml2ZSA8LSBjKDE6NSk7IHN0YXQuZml2ZSAgIyDsmKjsoJAg7J207JqpIOqwgOuKpQpzdGF0LnRlbiA8LSBjKDE6MTApOyBzdGF0LnRlbgoyc3RhdCA8LSAyMiAgICMg7Jik66WY67Cc7IOdKOyIq+yekOuKlCDrp6gg7JWe7JeQIOydtOyaqSDrtojqsIApCl9zdGF0IDwtICLsmKTrpZgiICAjIOyYpOulmOuwnOyDnShf64qUIOunqCDslZ7sl5Ag7J207JqpIOu2iOqwgCkKCiMjIOq4sOuzuCDrjbDsnbTthLAg7Jyg7ZiVCnN1Y2hpIDwtIDNeNTsgc3VjaGkKbW9kZShzdWNoaSkgICPsiJjsuZjtmJUKCm5vbmxpIDwtIDU+MTA7IG5vbmxpIAptb2RlKG5vbmxpKSAgI+uFvOumrO2YlQoKbXVuamEgPC0gIuyMjeuUsOyYtO2RnCI7IG11bmphCm1vZGUobXVuamEpICAj66y47J6Q7ZiVCgpib2tzbyA8LSAyKzRpOyBib2tzbwptb2RlKGJva3NvKSAgI+uzteyGjOyImO2YlQoKIyMg7Yq57IiYIOuNsOydtO2EsCDsnKDtmJUKIyBOQSA6IOyVhOyngSDrqqjrpbTripQg6rCSKG5vdCBhdmFpbGFibGUpLCDtlaDri7ntlZjsp4Ag7JWK7J2AIOqwkgojIE5VTEwgOiDqsJLsnbQg7KG07J6s7ZWY7KeAIOyViuydjAojIE5hTiA6IOyImO2VmeyggeycvOuhnCDsoJXsnZjqsIAg67aI6rCA64ql7ZWcIOyImChub3QgYSBudW1iZXIpIGVnKSAwLzAsIGxvZygtNSkKIyBpbmYgOiDslpHsnZgg66y07ZWc64yACiMgLWluZiA6IOydjOydmCDrrLTtlZzrjIAKeCA8LSBjKDAsNyw4KQp4L3ggICAjIE5hTiAxIDEKMS94ICAgIyBJbmYgMC4xNDI4NTcxIDAuMTI1MDAwMAoKIyMg642w7J207YSwIOycoO2YlSDqsoDspp0g7ZWo7IiYCmlzLm51bWVyaWMoc3VjaGkpICAjIOyImOy5mO2YlSDsl6zrtoAKaXMuY29tcGxleChtdW5qYSkgICMg7Iuk7IiY7ZiVIOyXrOu2gAppcy5pbnRlZ2VyKHgpICMg7KCV7IiY7ZiVIOyXrOu2gAppcy5sb2dpY2FsKHgpICMg64W866as7ZiVIOyXrOu2gAppcy5jb21wbGV4KHgpICMg67O17IaM7IiY7ZiVIOyXrOu3sAppcy5jaGFyYWN0ZXIoeCkgIyDrrLjsnpDtmJUg7Jes67aACgppcy5uYSh4KSAjIE5BIOyXrOu2gChOYU7rj4QgVHJ1ZeuhnCDrsJjtmZghKSAvIOydtCDsmbjsl5Drj4QgaXMubnVsbCh4KSwgaXMubmFuKHgpLCBpcy5maW5pdGUoeCksIGlzLmluZmluaXRlKHgpLCBpcy5tYXRyaXgoeCkKCnllc05BIDwtIGMoMiwzLCBOQSwgTlVMTCxOYU4pOyB5ZXNOQQppcy5uYSh5ZXNOQSkgCmlzLm51bGwoeWVzTkEpCmlzLm5hbih5ZXNOQSkKCgojIyDquLDrs7gg7Jyg7Yu466as7YuwCmxzKCkgIyBy7JeQ7IScIOyngOq4iOq5jOyngCDrp4zrk6TslrTrhpPsnYAg66qo65OgIOqwneyytOuTpCDrs7Tsl6zspIwKCnJtKGEpICMg7KeA7Jqw6rOgIOyLtuydgCDqsJ3ssrQg7KCc6rGwCgpybShsaXN0PWxzKCkpICAjIOuqqOuToCDqsJ3ssrQg7KCc6rGwCgpgYGAKCiMg67Kh7YSwCgpgYGB7ciAy7KO87LCofQojIOuyoe2EsOuhnCDtlansuZjripQg7J247J6QIOyekOyytOqwgCDrsqHthLDrqbQsIOuyoe2EsOulvCDtkoDslrTshJwg7ZWY64KY7J2YIO2Vqey5qAp2ZWNfMSA8LSBjKDEsMiwzKTsgdmVjXzEKdmVjXzIgPC0gYyg0OjYpOyB2ZWNfMgp2ZWNfMyA8LSBjKHJlcCg3LDMpKTsgdmVjXzMgCmModmVjXzIsdmVjXzEsdmVjXzMpICAjIFsxXSA0IDUgNiAxIDIgMyA3IDcgNwoKCiMjIOyImOyXtCDsg53shLHtlZjquLAgCjE6NyAgICMgbjptICA6IG7rtoDthLAgbeq5jOyngCAx64uo7JyE66GcIOymneqwgChvciDqsJDshowuIG4+beyduOqyveyasCntlZjripQg7IiY7Je0IOyDneyEsSBlZykgWzFdIDEgMiAzIDQgNSA2IDcKc2VxKGZyb209MSwgdG89MTUsIGJ5PTIpICAjIDHrtoDthLAgMTXquYzsp4AgMuuLqOychOuhnCDspp3qsIDtlZjripQg7IiY7Je0ICBlZykgWzFdICAxICAzICA1ICA3ICA5IDExIDEzIDE1CnNlcSgxLCAxNSwgMikgIyDsnITsmYAg6rCZ7J2AIOydmOuvuApzZXEoMSwgLTEsIGJ5PS0wLjUpICAjIOydjOydmCDrsKntlqXsnLzroZwg6rCIIOyImCDsnojsnYwgZWcpIFsxXSAgMS4wICAwLjUgIDAuMCAtMC41IC0xLjAKc2VxKDEsIDcsIGxlbmd0aD0zKSAgIyDquLjsnbTqsIAgM+ydtCDrkJjqsozrgZQuIGVnKSBbMV0gMSA0IDcKCnJlcCgyLCB0aW1lcz01KSAgIyAy66W8IOuLpOyEr+uyiCDrsJjrs7UgIGVnKSBbMV0gMiAyIDIgMiAyCnJlcChjKDEsMiwzKSwzKSAgIyDsoITssrTrpbwgM+uyiCDrsJjrs7UodGltZXM9MyksICAxIDIgMyAxIDIgMyAxIDIgMwpyZXAoYyg0LDIsNSksIHRpbWVzPWMoNCwzLDQpKSAgIyA064qUIDTrsogsIDLripQgM+uyiCwgNeuKlCA067KIIOuwmOuztS4gIGVnKSBbMV0gNCA0IDQgNCAyIDIgMiA1IDUgNSA1CnJlcCgxOjQsIGVhY2g9MikgICMg6rCB6rCBIDLrsojslKkg67CY67O1LCAgWzFdIDEgMSAyIDIgMyAzIDQgNApyZXAoMTo0LCB0aW1lcz0yKSAjIOyghOyytOulvCAy67KIIOuwmOuztS4gIFsxXSAxIDIgMyA0IDEgMiAzIDQKcmVwKGMoMywyLDUpLCBsZW5ndGg9NSkgICMg6ri47J206rCAIDXqsIAg65CY6rKMIOuwmOuztSAgICBlZykgWzFdIDMgMiA1IDMgMgoKCiMjIOuyoe2EsCDsnbjrjbHsi7EgOiDsm5Dshowg7ISg7YOd7ZWY6riwCnZlY19pbiA8LSBjKDEsMyw1LDcsOSwxMSwxMywxNSk7IHZlY19pbgp2ZWNfaW4gPC0gc2VxKDEsIDE1LCAyKTsgdmVjX2luCnZlY19pbls1XSAgIyA167KI7Ke47JeQIOyeiOuKlCDsm5Dshowg7ISg7YOdCgp2ZWNfaW5bMTo0XSAjIDF+NOuyiOynuCDsm5DshowKdmVjX2luWzE6NCw4XSAgI+yXkOufrAp2ZWNfaW5bYygxOjQsOCldICAjIOuzteyImOydmCDqsJLsnYQg7ISg7YOd7ZWY6rOgIOyLtuycvOuptCDrsqHthLDroZwg7J24642x7IuxISEgIDF+NOuyiOynuCwgOOuyiOynuCDsm5DshowKCnZlY19pblstMl0gICMgMuuyiOynuCDsm5Dshowg7KCc7Jm4CnZlY19pblstKDE6NSldICAjIDF+NeuyiOynuCDsm5Dshowg7KCc7Jm4CnZlY19pblstYygxOjUsOCldICMgMX4167KI7Ke4LCA467KI7Ke4IOybkOyGjCDsoJzsmbgKCiMg7KGw6rG07JeQIOunnuuKlCDsm5DshowgLT4g64W866as7ZiVIOuyoe2EsCDsnbTsmqkKdmVjX2luW3ZlY19pbjwxMF0gIyAxMOuztOuLpCDsnpHsnYAg7JuQ7IaM65OkCgojIOydtOumhOu2meyXrOynhCDsm5DshowgLT4g7J2066aEKG5hbWVzKSDsnbTsmqkKcmVnaW9uX2NvZGUgPC0gYygiMDIiLCIwMzEiLCIwNTUiLCIwNjMiLCIwNjQiKSAgCm5hbWVzKHJlZ2lvbl9jb2RlKSA8LSBjKCJTZW91bCIsIkd5ZW9uZ2dpIiwiR3llb25nbmFtIiwiSmVvbmJ1ayIsIkplanUiKSAgIyDqsIEg7JuQ7IaM7J2YIOydtOumhOydhCDsoJXsnZgKcmVnaW9uX2NvZGUKcmVnaW9uX2NvZGVbIkd5ZW9uZ2dpIl0KcmVnaW9uX2NvZGVbMl0gICMg7J24642x7Iqk66GcIO2VtOuPhCDsg4HqtIDsl4bquLQg7ZWoCnJlZ2lvbl9jb2RlW2MoIlNlb3VsIiwiSmVqdSIpXQpyZWdpb25fY29kZVtjKDEsNSldCgojIOyXsOyKtQpmcnVpdCA8LSBjKCJhcHBsZSIsImJlcnJ5IiwiY2hlcnJ5IiwiZ3JhcGUiLCJsZW1vbiIsIm9yYW5nZSIsInBlYWNoIikKZnJ1aXRbYygxLDMpXVsxXSAgIyAxfjPrsojsp7gg7JuQ7IaMIOykkSAx67KI7Ke4IOybkOyGjCAiYXBwbGUiCmZydWl0W2MoMSwxLDMsMywzLDMpXSAgIyAiYXBwbGUiICAiYXBwbGUiICAiY2hlcnJ5IiAiY2hlcnJ5IiAiY2hlcnJ5IiAiY2hlcnJ5IgoKCiMjIOuyoe2EsCDrjbDsnbTthLAg7LaU6rCA7ZWY6riwIApvbGQgPC0gYygyLDQsNiw4KTsgb2xkCm9sZCA8LSBjKG9sZCwxMCk7IG9sZCAgIyDrkqTsl5Ag7JuQ7IaMIDEwIOy2lOqwgApvbGQgPC0gYyhjKC0yLDApLG9sZCk7IG9sZCAgIyDslZ7sl5Ag7JuQ7IaMIC0yLCAwIOy2lOqwgApvbGRbMTBdIDwtIDIwIDtvbGQgICMgMTDrsojsp7gg7J6Q66as7J2YIOybkOyGjOulvCAyMOycvOuhnCAo7JuQ7IaM6rCAIOyXhuuKlCDrtoDrtoTsnYAg7J6Q64+Z7Jy866GcIE5B7LKY66asIOuQqCkKCgojIyDrsqHthLAg7Jew7IKwIDog6rCZ7J2AIOyekOumrOyXkCDsnojripQg7JuQ7IaM64G866asIOyXsOyCsCDsiJjtlontlaguIAp2IDwtIGMoMTEsMTIsMTMsMTQsMTUpOyB2CncgPC0gYyggMSwgMiwgMywgNCwgNSk7IHcKdit3CnYtdwp2KncgICMgWzFdIDExIDI0IDM5IDU2IDc1CnYvdwp3XnYKCiMg6ri47J206rCAIOyEnOuhnCDri6Trpbgg67Kh7YSw66W8IOyXsOyCsO2VnOuLpOuptD8gLT4g66qo7J6Q656AIOyqveydmCDrsqHthLDqsIAg64uk7IucIOyyq+uyiOynuCDsm5DshozroZwg64+M7JWE6rCA7IScIOyerO2ZnOyaqe2VqCAKcmVjeWNsZS42IDwtIGMoMTo2KQpyZWN5Y2xlLjMgPC0gYygxOjMpCnJlY3ljbGUuNityZWN5Y2xlLjMgICMgWzFdIDIgNCA2IDUgNyA5IDwtIDErMSAyKzIgMyszIDQrMSA1KzIgNiszIOq8tOuhnCDrkJjripQg6rKDCgoKIyMg7Jew7Iq166y47KCcCiMgMSkgMX4xMDDquYzsp4DsnZgg67KU7JyEIOuCtOyXkOyEnCA17J2YIOqwhOqyqeydhCDqsJbripQg6rCSIOyDneyEsQp4PXNlcShmcm9tPTEsdG89MTAwLGJ5PTUpCngKIyAyKSDsnITsl5DshJwg7IOd7ISx7ZWcIOyekOujjOulvCDsl63siJzsnLzroZwg64KY7YOA64K06riwCnhbbGVuZ3RoKHgpOjFdCnJldih4KSAjIHJldmVyc2UKCiMgMykgMX406rmM7KeA7J2YIOqwkuydhCDqsIHqsIEgMuuyiCwgM+uyiCwgMuuyiCwgM+uyiCDrsJjrs7XtlZjripQg67Kh7YSwIOyDneyEsQp4PC1jKDE6NCkKcmVwKHgsdGltZXM9YygyLDMsMiwzKSkKcmVwKGMoMTo0KSxjKDIsMywyLDMpKQpjKHJlcCgxLDIpLCByZXAoMiwzKSwgcmVwKDMsMiksIHJlcCg0LDMpKQoKIyA0KSAxfjEwMDDquYzsp4DsnZgg67KU7JyEIOuCtOyXkOyEnCDquLjsnbTqsIAgMTLqsIAg65CY6rKMIOydvOygle2VnCDqsITqsqnsnYQg6rCW64qUIOqwkuuTpOydhCDsg53shLHtlZjsi5zsmKQKc2VxKGZyb209MSwgdG89MTAwMCwgbGVuZ3RoPTEyKQpzZXEoMSwgMTAwMCwgbGVuZ3RoPTEyKQpzZXEoMSwgMTAwMCwgMTIpICMgbGVuZ3Ro66W8IOyngOyglSDslYjtlbTso7zrqbQgZGVmYXVsdOyduCBieSDquLDriqXsnbQg7IKs7Jqp65CoKDEy7JSpIOymneqwgCkKCgpgYGAKCmBgYHtyIDPso7zssKggOiDrsqHthLAg6rSA66CoIOyXrOufrCDtlajsiJjrk6R9CnJtKGxpc3Q9bHMoKSkKCiMjIOusuOyekO2YlSDrsqHthLDsl5Ag7KCB7Jqp7ZWY64qUIO2VqOyImCAKCiMgbmNoYXIoeCkgOiDrrLjsnpDsl7QgeOulvCDqtazshLHtlZjripQg66y47J6Q7J2YIOqwnOyImAp3b3Jkc18yIDwtIGMoIlIiLCAiUHl0aG9uIiwgIlNBUyIpOyB3b3Jkc18yCm5jaGFyKHdvcmRzXzIpICMgIFsxXSAxIDYgMwoKIyBwYXN0ZSguLi4gLCBzZXA9IiIsIGNvbGxhcHNlPSIiKSA6IOusuOyekOyXtOydmCDqsrDtlakuIGNvbGxhcHNl64qUIOqysOqzvOqwkuydtCDsl6zrn6zqsJzsnZgg67Kh7YSw7J2865WMIO2VmOuCmOydmCDrsqHthLDroZwg7ZWp7LmY64qUIOyXre2VoAojIHBhc3RlMCguLi4pIDogcGFzdGUoLi4uICwgc2VwPSIiKSDsmYAg64+Z7J28CnBhc3RlKGMoJ+yyqycsJ+uRkCcsJ+yEuCcsJ+uEpCcsJ+uLpOyErycpLCByZXAoJ+uyiOynuCcsIDUpLCBzZXA9JycpICAjIFsxXSAi7LKr67KI7Ke4IiAgICLrkZDrsojsp7giICAgIuyEuOuyiOynuCIgICAi64Sk67KI7Ke4IiAgICLri6TshK/rsojsp7giCnBhc3RlKGMoJ+yyqycsJ+uRkCcsJ+yEuCcsJ+uEpCcsJ+uLpOyErycpLCByZXAoJ+uyiOynuCcsIDUpLCBzZXA9JycsIGNvbGxhcHNlPScsICcpICAjIFsxXSAi7LKr67KI7Ke4LCDrkZDrsojsp7gsIOyEuOuyiOynuCwg64Sk67KI7Ke4LCDri6TshK/rsojsp7giCgojIHN1YnN0cih4LHN0YXJ0LHN0b3ApOiDrrLjsnpDsl7TsnZgg7J2867aA67aEIOyEoO2DnQpzdWJzdHIoIlN0cmF3QmVycnkiLHN0YXJ0PTYsc3RvcD0xMCkgICMgWzFdICJCZXJyeSIKc3Vic3RyKCJTdHJhd0JlcnJ5Iiw2ICwxMCkKYmVycnkgPC0gYygiQmx1ZUJlcnJ5IiwiQ3JhbkJlcnJ5Iik7IGJlcnJ5CnN1YnN0cihiZXJyeSw1LDkpICAgIyDqsIEg7JqU7IaM7J2YIOuLpOyEr+u2gO2EsCDslYTtmYkg67KI7Ke4IOyekOumrOydmCDqsJLrp4wg672R6riwIApzdWJzdHIoYmVycnksYygxLDUpLGMoNCw5KSkgICAjICDssqvrsojsp7gg7JqU7IaM64qUIDEtNOuyiOynuCwg65GQ67KI7Ke4IOyalOyGjOuKlCA1LTnrsojsp7gg7J6Q66asLiBbMV0gIkJsdWUiICAiQmVycnkiCgojIHN0cnNwbGl0KHgsIHNwbGl0KSA6IHNwbGl0IOq4sOykgOycvOuhnCDrrLjsnpDsl7Qg67aE66asIApjYWZlIDwtIGMoImhvdCxjaG9jbyIsImljZSxhbWVyaWNhbm8iLCJob3QsbW9jY2EiLCJpY2UsbGF0dGUiKTsgY2FmZQpzdHJzcGxpdChjYWZlLHNwbGl0PSIsIikKbW9kZShzdHJzcGxpdChjYWZlLHNwbGl0PSIsIikpICMg642w7J207YSwIOq1rOyhsCA6IGxpc3QKc3Ryc3BsaXQoImdhb25udXJpIixzcGxpdD0iIikgICMgWzFdICJnIiAiYSIgIm8iICJuIiAibiIgInUiICJyIiAiaSIKCgojIHN1YihvbGQsIG5ldywgeCkgOiB466y47J6Q7Je0IOykkSDssqvrsojsp7ggb2xk66eMIG5ld+uhnCDsiJjsoJUKIyBnc3ViKG9sZCwgbmV3LCB4KSA6IOuqqOuToCBvbGTrpbwgbmV3IOuhnCDsiJjsoJUgKGdsb2JhbCBzdWIpCmhhcHB5X2VuZCA8LSAiSSBoYXRlIHlvdS4gWW91IGhhdGUgbWUiOyBoYXBweV9lbmQKc3ViKCJoYXRlIiwibG92ZSIsaGFwcHlfZW5kKQpnc3ViKCJoYXRlIiwibG92ZSIsaGFwcHlfZW5kKQoKCiMjIOuyoe2EsCDsm5Dshowg7IK97J6FIOyCreygnCDqtIDroKgg7ZWo7IiYIAojIHJlcGxhY2UoeCwgaW5kZXgsIHZhbHVlcykgOiAgeOydmCBpbmRleOyXkCDtlbTri7ntlZjripQg7Iic67KI7J2YIOqwkuydhCB2YWx1ZXMg6rCS7Jy866GcIOuzgOqyvSAKb25lMnRocmVlIDwtIGMoIm9uZSIsInR3byIsInRocmVlIik7IG9uZTJ0aHJlZQpvbmUydGhyZWUgPC0gcmVwbGFjZShvbmUydGhyZWUsMywzKTsgb25lMnRocmVlCm9uZTJ0aHJlZSA8LSByZXBsYWNlKG9uZTJ0aHJlZSwxOjIsYygxLDIpKTsgb25lMnRocmVlIAoKIyBhcHBlbmQoeCwgdmFsdWVzLCBhZnRlcikgOiB47JeQIHZhbHVlc+ulvCBhZnRlcuyXkCDtlbTri7ntlZjripQg7Iic67KIIOuSpOyXkCDrhKPsnYwgCm9uZTJ0aHJlZSA8LSBhcHBlbmQob25lMnRocmVlLCIxLjUiLGFmdGVyPTEpOyBvbmUydGhyZWUKb25lMnRocmVlIDwtIGFwcGVuZChvbmUydGhyZWUsc2VxKDIuMSwyLjksMC4xKSxhZnRlcj0zKTsgb25lMnRocmVlCgojIHNvcnQoeCwgZGVjcmVhc2luZyA9IEYsIC4uLikgOiB4IOuNsOydtO2EsOulvCDsmKTrpoTssKjsiJwg7KCV66CsKGRlY3JlYXNpbmcgPSBUIOuptCDrgrTrprzssKjsiJwpIApudW0gPC0gYyg5LDEsMiw0LDIsNCw2LDgsNSwzLDIsMSw5LDYsMTAsMSwyKTsgbnVtCnNvcnQobnVtKSAjIOyYpOumhOywqOyInCAo7JWM7YyM67Kz7IicLCDqsIDrgpjri6TsiJwpCnNvcnQobnVtLGRlY3JlYXNpbmc9VCkgICMg64K066a87LCo7IicIAojIO2VnOq4gOqzvCDsiKvsnpDrpbwg6rCZ7J20IOyTsOuptCDsiKvsnpDrpbwg66y47J6Q66GcIOy3qOq4iSDtm4Qg7KCV66CsCgojIG9yZGVyKHgsIGRlY3JlYXNpbmc9RiwgLi4uKSA6IHgg7J2YIOuNsOydtO2EsOulvCDsmKTrpoTssKjsiJzsnLzroZwg7KCV66Cs7ZWY6rOgIOybkOuemOydmCAi7JyE7LmYIuulvCDstpzroKUgCmFiYyA8LSBjKCJmIiwiZCIsImIiLCJlIiwiYSIsImMiKTsgYWJjCm9yZGVyKGFiYykgICMgWzFdIDUgMyA2IDIgNCAxICDsnbjrjbHsiqTrpbwg67CY7ZmY7ZWoISEgCnNvcnQoYWJjKSAgICMgWzFdICJhIiAiYiIgImMiICJkIiAiZSIgImYiCgoKIyMg642w7J207YSwIO2Yle2DnCDrs4DtmZggCmFzLm51bWVyaWMoYygiMSIsIjIiKSkgICPrrLjsnpDtmJUgdG8g7IiY7LmY7ZiVIAphcy5udW1lcmljKGMoImEiLCJiIikpICAj6rCV7KCcIOuzgO2ZmOyXkCDsnZjtlbQgTkHqsJIg7IOd7ISxCmFzLm51bWVyaWMoYyhUUlVFLEZBTFNFKSkgICPrhbzrpqztmJUgdG8g7IiY7LmY7ZiVCgphcy5jaGFyYWN0ZXIoYygxLC0xKSkgICPsiJjsuZjtmJUgdG8g66y47J6Q7ZiVIAphcy5jaGFyYWN0ZXIoYyhULEYpKSAgI+uFvOumrO2YlSB0byDrrLjsnpDtmJUKCmFzLmxvZ2ljYWwoYygwLDEsMiw1NTUsOTk5LC0xMTEpKSAgIyAwKEZBTFNFKSDsnbTsmbjsnZgg7IiY64qUIOuLpCBUUlVFCmFzLmxvZ2ljYWwoYygiVFJVRSIsIlQiLCJGQUxTRSIsIkYiKSkgICPrrLjsnpDtmJUgdG8g64W866as7ZiVCmFzLmxvZ2ljYWwoYygieWVzIiwibm8iLCJnb29kIiwiYmFkIikpICAjIFRSVUUoVCksRkFMU0UoRikg7J207Jm47J2YIOusuOyekOuKlCBOQQoKYXMuZmFjdG9yKGMoMSwyLDIsMywzLDMsNCw0KSkgICPsiJjsuZjtmJUgdG8g7JqU7J24CmFzLmZhY3RvcihjKCJBIiwiQSIsIkIiLCJDIiwiQiIpKSAgI+usuOyekO2YlSB0byDsmpTsnbgKIyBhcy52ZWN0b3IoKSwgYXMubWF0cml4KCksIGFzLmRhdGEuZnJhbWUoKSDrj4Qg7KG07J6sIAoKCiMjIG1vZGUoKSB2cyBjbGFzcygpCiMgbW9kZSgpIDog66y866as7KCB7J24IOyekOujjO2YlSAo7IiY7LmY7ZiVLCDrrLjsnpDtmJUsIOumrOyKpO2KuCwg7ZWo7IiYIOuTsSkKIyBjbGFzcygpIDog7LaU7IOB7KCB7J24IOyekOujjO2YlSAo6rCd7LK07KeA7ZalIOq0gOygkCwg7ZW07ISd7J20IOuLpOyWke2VqCkKZGF0ZSA8LSBhcy5EYXRlKCIyMDE5LTAzLTE4Iik7IGRhdGUgICPrgqDsp5ztmJXsnLzroZwg67OA7ZmYCm1vZGUoZGF0ZSkgICPsiJjsuZjtmJUKY2xhc3MoZGF0ZSkgICPrgqDsp5ztmJUKCm1hdCA8LSBtYXRyaXgoYygiYSIsImIiLCJjIiwiZCIpLG5yb3c9Mik7IG1hdCAgI+2WieugrAptYXQyIDwtIG1hdHJpeChjKDEsMiwzLDQpLG5yb3c9Mik7IG1hdCAgI+2WieugrAptb2RlKG1hdCkgICPrrLjsnpDtmJUKbW9kZShtYXQyKSAgI+yImOy5mO2YlQpjbGFzcyhtYXQpICAj7ZaJ66CsCmNsYXNzKG1hdDIpICAj7ZaJ66CsCmBgYAoKCiMg7JqU7J24KEZhY3RvcikKYGBge3IgM+yjvOywqCA6IOyalOyduH0KIyMgZmFjdG9yKCkgOiDrspTso7ztmJUg642w7J207YSw7JeQIOyCrOyaqSEgKOuyoe2EsOydmCDqtazshLHsmpTshozrk6TsnbQg67CY67O165CY64qUIOqyveyasCDsmpTsnbjsnYQg7IKs7Jqp7ZWY64qUIOqyg+ydtCDsoIDsnqXtlZjripTrjbAg7Zqo7Jyo7KCBKQpzcGVjaWVzIDwtIGMoImFuaW1hbCIsImZpc2giLCJiaXJkIiwiYW5pbWFsIiwiZmlzaCIsImJpcmQiLCJhbmltYWwiLCJmaXNoIiwiYmlyZCIpOyBzcGVjaWVzCnNwZWNpZXMgPC0gZmFjdG9yKHNwZWNpZXMpOyBzcGVjaWVzICAjbGV2ZWxzKOuylOyjvCnripQg7JWM7YyM67Kz7IicCnNpemUgPC0gYyg2MCwyMCw0LDc2LDMwLDIsNzAsMjUsNSk7IHNpemUKbGV2ZWxzKHNwZWNpZXMpICAjIGxldmVsIOunjCDtmZXsnbgKbmxldmVscyhzcGVjaWVzKSAgIyBsZXZlbCDqsK/siJgg7ZmV7J24CnRhcHBseShzaXplLHNwZWNpZXMsbWVhbikgIyDqsIEgbGV2ZWwg67OEIG1lYW7snLzroZwg67CY7ZmYIAoKc3BlY2llcyA8LSBjKCJhbmltYWwiLCJmaXNoIiwiYmlyZCIsImFuaW1hbCIsImZpc2giLCJiaXJkIiwiYW5pbWFsIiwiZmlzaCIsImJpcmQiKTsgc3BlY2llcwpzcGVjaWVzIDwtIGZhY3RvcihzcGVjaWVzLCBsZXZlbHM9YygiZmlzaCIsICJiaXJkIiwgImFuaW1hbCIpKTsgc3BlY2llcyAgI2xldmVscyDsiJzshJwg7KeA7KCVCmxldmVscyhzcGVjaWVzKQoKIyMg6re466O567OEIOyalOyVvQojIHRhcHBseShYLCBJbmRleCwgRlVOKSAKIyBYIDog6rOE7IKwIOuMgOyDgeydtCDrkJjripQg67Kh7YSwCiMgSW5kZXggOiDqt7jro7nsnYQg64KY64iE6riwIOychO2VnCDquLDspIAKIyBGVU4gOiDsoIHsmqntlaAg7ZWo7IiYIGV4IDogbWVhbiwgc3VtLCBsZW5ndGgsIHNkCgojIyDsl7DsirXrrLjsoJwgCmNhdCgiXG4tLS0tLS0tLS0tLS0tXG4iKQphbmltYWw8LXJlcChjKCJkb2ciLCAiY2F0IiwgInJhYmJpdCIpLGMoMywyLDQpKTthbmltYWwKYWdlPC1jKDEwLDE0LDYsNyw5LDQsNiwyLDgpO2FnZQphbmltYWw8LWZhY3RvcihhbmltYWwpO2FuaW1hbAp0YXBwbHkoYWdlLCBhbmltYWwsIHNkKQoKCmBgYAojIO2WieugrCAobWF0cml4KQpgYGB7ciA07KO87LCofQpybShsaXN0PWxzKCkpCgojIyDtlonroKwg7IOd7ISx7ZWY6riwCiMgbWF0cml4KGRhdGEsIG5yb3c9MSwgbmNvbD0xLCBieXJvdz1GLCBkaW1uYXZlcz1OVUxMKSAKQSA8LSBtYXRyaXgoYygxLDIsMyw0LDUsNiw3LDgsOSksbnJvdz0zLG5jb2w9Myk7IEEgICMg7Je06riw7KSA7Jy866GcIOyxhOybgCjssqvrsojsp7gg7Je067aA7YSwIHx8fCApCkEgPC0gbWF0cml4KGMoMSwyLDMsNCw1LDYsNyw4LDkpLG5yb3c9MyxuY29sPTMsYnlyb3c9VCk7IEEgICMg7ZaJIOq4sOykgOycvOuhnCDssYTsm4AgKOyyq+uyiOynuCDtlonrtoDthLAg7LCo66GA66GcKQpBIDwtIG1hdHJpeChjKDEsMiwzLDQsNSw2LDcsOCw5KSxucm93PTMsbmNvbD0zLGJ5cm93PVQsIGRpbW5hbWVzID0gbGlzdChjKCJyb3cxIiwgInJvdzIiLCAicm93MyIpLCBjKCJjb2wxIiwgImNvbDIiLCAiY29sMyIpKSk7IEEKY2xhc3MoQSkgICMgWzFdICJtYXRyaXgiICJhcnJheSIgCgojIGFycmF5KGRhdGEsIGRpbT1sZW5ndGgoZGF0YSksIGRpbW5hbWVzPU5VTEwpCmRhdCA8LSBjKDEsMiwzLDIsNCw2LDMsNiw5LDQsOCwxMiw1LDEwLDE1KTsgZGF0CkQgPC0gYXJyYXkoZGF0LGRpbT1jKDMsNSkpOyBECkQgPC0gYXJyYXkoZGF0LGMoMyw1KSk7IEQgCiMgYXJyYXkg7ZWo7IiY64qUIG1hdHJpeCDtlajsiJjsnZggYnlyb3fsmYAg6rCZ7J2AIOyYteyFmOydtCDsl4bquLAg65WM66y47JeQIOyeheugpSDrsqHthLDrpbwg66+466asIOyggeygiO2eiCDsiJzshJzrpbwg67CU6r+U7IScIOuEo+yWtOykmOyVvCDtlago7LKr67KI7Ke4IO2Wieu2gO2EsCkKY2xhc3MoRCkKCiMgcmJpbmQoKSA6IO2WieycvOuhnCDqsrDtlakgKOychCDslYTrnpjroZwpCiMgY2JpbmQoKSA6IOyXtOuhnCDqsrDtlako7KKMIOyasOuhnCkKcjEgPC0gYygxOjUpOyByMQpyMiA8LSBzZXEoMiwxMCwyKTsgcjIKcjMgPC0gcmVwKDMsNSk7IHIzCnJiIDwtIHJiaW5kKHIxLHIyLHIzKTsgcmIKY2xhc3MocmIpCgpjMSA8LSBjKDEsMiwzKTsgYzEKYzIgPC0gNTo3OyBjMgpjMyA8LSByZXAoOCwzKTsgYzMKYzQgPC0gc2VxKDksMTQsbGVuZ3RoPTMpOyBjNApjYiA8LSBjYmluZChjMSxjMixjMyxjNCk7IGNiCmNsYXNzKGNiKQoKCiMjIO2WieqzvCDsl7TsnZgg6rCc7IiY66W8IOyEuOuKlCDtlajsiJgKIyBucm93KCksIG5jb2woKSA6IO2WiSwg7Je07J2YIOqwnOyImAojIGRpbSgpIDog642w7J207YSw7J2YIO2WiSwg7Je0LCDsuLXsnZgg7IiYCgoKIyMg7ZaJ66Cs7JeQ7IScIOq1rOyEseyalOyGjCDshKDtg53tlZjquLAgLT4gQVvtloksIOyXtF0KQSA8LSBtYXRyaXgoYygxLDIsMyw0LDUsNiw3LDgsOSksIG5yb3c9MywgYnlyb3c9VCk7IEEKQVsyLDNdICMgMu2WiSAz7Je0IOyalOyGjApBW2MoMSwyKSwzXSAgIyAxLDLtlonsnZggM+yXtOyXkCDsnojripQg7JqU7IaM65OkCkFbMSxdICAjIDHtlokg66qo65OgIOyXtApBWywyXSAgIyDrqqjrk6Ag7ZaJIDLsl7QKQVssM11bMl0gICMg66qo65OgIO2WiSAz7Je07J2YIDLrsojsp7gg7JuQ7IaMCgoKIyMg7ZaJ66Cs7J2YIOq4sOuzuOyXsOyCsApYIDwtIG1hdHJpeChjKDQsOCwxMiwxNiksbnJvdz0yKTsgWApZIDwtIG1hdHJpeChjKDMsNiw5LDEwKSxucm93PTIpOyBZClgrWQpYLVkKWC9ZClgqWSAgI+ybkOyGjOuBvOumrCDqs7EKWF5ZClglJVkgIyDrgpjrqLjsp4AgCgpYJSolWSAgI+2WieugrCDqs7EKdChYKSAjIOyghOy5mCDtlonroKwKc29sdmUoWCkgICMg7JetIO2WieugrApkZXQoWCkgIyDtlonroKzsi50KZGlhZyhYKSAjIOuMgOqwge2WieugrCAo64yA6rCB7JuQ7IaM65OkIOygnOyZuO2VnCDsm5Dshozrk6TsnYAg7KCE67aAIDDsnLzroZwpCgpyb3dTdW1zKFgsIG5hLnJtPVQpICMg7ZaJ67OEIO2VqSAo6rCZ7J2AIO2WieyXkCDsnojripQg7JuQ7IaM65OkIO2Vqe2VnCDqsrDqs7wpLCBuYS5ybT1U66GcIOqysOy4oeqwkiDrrLTsi5ztlZjqs6Ag7Jew7IKwKOuUlO2PtO2KuOuKlCBGKQpjb2xTdW1zKFgsIG5hLnJtPVQpICMg7Je067OEIO2VqSAo6rCZ7J2AIOyXtOyXkCDsnojripQg7JuQ7IaM65OkIO2Vqe2VnCDqsrDqs7wpCnJvd01lYW5zKEEsIG5hLnJtPVQpICMg7ZaJ67OEIO2Pieq3oApjb2xNZWFucyhBLCBuYS5ybT1UKSAjIOyXtOuzhCDtj4nqt6AKCkIgPC0gbWF0cml4KGMoMiwxLDQsMiksIG5yb3c9Mik7Qgpzb2x2ZShCKSAjIO2WieugrOyLneydmCDqsJLsnbQgMOydtOyXrOyEnCDsl63tlonroKwg7KG07J6sIFgKRSA8LSBtYXRyaXgoMTo2LCBucm93PTMpO0UKc29sdmUoRSkgIyDsoJXrsKntlonroKzsnbQg7JWE64uI7Jes7IScIOyXre2WieugrCDsobTsnqwgWAoKaWRlbnRpdHkgPC0gcmVwKDEsNSkKZGlhZyhpZGVudGl0eSkgIyBpZGVudGl0eeulvCDrjIDqsIEg7JuQ7IaM66GcIOqwluuKlCDrjIDqsIEg7ZaJ66CsICjri6jsnIQg7ZaJ66CsKQp2YWx1ZTwtYygyLDQsNSw5KQpkaWFnKHZhbHVlKQoKCiMjIGFwcGx5KFgsIG1hcmdpbiwgZnVuLCAuLi4pICA6IO2WieugrOyXkCDsl7DsgrAg7KCB7JqpISAKIyBtYXJnaW4gOiDtlajsiJjrpbwg7Ja065akIOuwqe2WpeycvOuhnCDsoIHsmqntlaDsp4AuIDEocm93cywg6rCZ7J2AIO2WieyXkOyEnCkgIG9yIDIoY29sdW1ucykKIyBmdW4gOiDsoIHsmqntlaAg7ZWo7IiYLiBtZWFuLCB2YXIsIC4uLgptYXQgPC0gbWF0cml4KGMoNCwzLDEsNywxMCw5LDgsMTIsMTYpLG5yb3c9Myk7IG1hdAphcHBseShtYXQsMSxzdW0pICAjMTogcm93cwphcHBseShtYXQsMixzdW0pICAjMjogY29sdW1ucwphcHBseShtYXQsMSxmdW5jdGlvbih4KXt4XjIrMX0pICAjIOuyoe2EsOulvCDstpzroKXtlaDrlYwg7ZaJ7JeQIOqysOyzkOyEnCDtlajsiJjrpbwg7Iuk7ZaJ7ZW064+EIOqysOqzvOuKlCDsl7TroZwg6rKw7ZWp7ZW07IScIOuztOyXrOykjCEo6rCBIOybkOyGjOyXkCDsoIHsmqntlZwg6rKw6rO866W8IOyghOy5mO2WieugrCDqvLTroZwg67O07Jes7KO864qU6rKDLi4g6reA7LCu6rKMIOyLnOumrC4uICkKYXBwbHkobWF0LDIsZnVuY3Rpb24oeCl7eF4yKzF9KQoKCiMjIO2WiSwg7Je07JeQIOydtOumhCDrtpnsnbTquLAKIyByb3duYW1lcyjtlonroKwpID0gYyjtlonsnbTrpoQpLCBjb2xuYW1lcyjtlonroKwpID0gYyjsl7TsnbTrpoQpCnNjb3JlIDwtIG1hdHJpeChjKDUsNywzLDQsMiw5KSxucm93PTMpOyBzY29yZQpyb3duYW1lcyhzY29yZSkgPC0gYygiRG9uZ0h5dW4iLCJIeXVuSmkiLCJZb3VLeXVuZyIpO3Njb3JlICAj7ZaJIOydtOumhApjb2xuYW1lcyhzY29yZSkgPC0gYygibWlkdGVybSIsImZpbmFsIik7c2NvcmUgICPsl7Qg7J2066aECgojIGRpbW5hbWVzKO2WieugrCkgPSBsaXN0KGMo7ZaJ7J2066aEKSwgYyjsl7TsnbTrpoQpKSAKbWlkIDwtIG1hdHJpeChjKDYsNywzLDIsMyw0KSxucm93PTMpOyBtaWQKZGltbmFtZXMobWlkKSA8LSBsaXN0KGMoIkRvbmdIeXVuIiwiSHl1bkppIiwiWW91S3l1bmciKSxjKCJzdGF0aXN0aWNzIiwibWF0aG1hdGljcyIpKQptaWQKCiMg7JWE64uI66m0IOqxjSDrp4zrk6TrlYzrtoDthLAg7J2066aEIOyngOyWtOyjvOq4sApmaW4gPC0gbWF0cml4KGMoOCw0LDMsMiw1LDkpLG5yb3c9MyxkaW1uYW1lcz1saXN0KCJuYW1lIj1jKCJEb25nSHl1biIsIkh5dW5KaSIsIllvdUt5dW5nIiksInN1YmplY3QiPWMoInN0YXRpc3RpY3MiLCJtYXRobWF0aWNzIikpKQpmaW4KCgojIyDsl7DsirXrrLjsoJwgCiMxKQpBIDwtIG1hdHJpeChjKDIsNiw0LDgsNywzLDEsNSw5KSwgYnlyb3c9VCwgbnJvdz0zKTtBCiMyKQpzb2x2ZShBKQpBJSolc29sdmUoQSkKc29sdmUoQSklKiVBCiMzKQphcHBseShBLDIsbWVhbikKYXBwbHkoQSwyLHZhcikKCgpgYGAKCiMg642w7J207YSwIO2UhOugiOyehApgYGB7ciA17KO87LCofQpybShsaXN0PWxzKCkpCgojIGRhdGEuZnJhbWUoKSA6IOuNsOydtO2EsCDtlITroIjsnoQg7IOd7ISxCm51bSA8LSBjKDE6NSk7IG51bQpjaGEgPC0gYygi6rCAIiwi64KYIiwi64ukIiwi6528Iiwi66eIIik7IGNoYQpsb2dpIDwtIGMoVCxGLFQsVFJVRSxGQUxTRSk7IGxvZ2kKZGZfMSA8LSBkYXRhLmZyYW1lKG51bSxjaGEsbG9naSk7IGRmXzEKc3RyKGRmXzEpICMg642w7J207YSw7J2YIOq1rOyhsCDtmZXsnbgKZGZfMiA8LSBkYXRhLmZyYW1lKG51bSxjaGEsbG9naSxhbHA9YygiYSIsImIiLCJjIiwiZCIsImUiKSk7IGRmXzIgIyDsnbTrn7Dsi53snLzroZwg6re464OlIOuzgOyImCDrsJTroZwg7LaU6rCA7ZWgIOyImCDsnojsnYwgCgoKIyMg7ZaJLCDsl7Qg7J2066aEIOyEpOyglSAKIyByb3cubmFtZXMg7Ji17IWYIOydtOyaqSAKZGZkZiA8LSBkYXRhLmZyYW1lKGNvbDE9MToyLCBjb2wyPTM6NCk7IGRmZGYKZGZkZiA8LSBkYXRhLmZyYW1lKGNvbDE9MToyLCBjb2wyPTM6NCwgcm93Lm5hbWVzPWMoInJvdzEiLCAicm93MiIpKTsgZGZkZgojIHJvd25hbWVzKGRmKSA9IGMoIuyWtOypjOq1rCIsICLsoIDsqYzqtawiKSDsnbTrn7Ag7Iud7Jy866GcIOuwlOq/gOyImOuPhCDsnojsnYwgCgojIG5hbWVzKCkgOiDsl7Qg7J2066aEIOuwmO2ZmApuYW1lcyhkZmRmKSAgCm5hbWVzKGRmZGYpIDwtIGMoIm5ld19jb2wxIiwgIm5ld19jb2wyIikgIyDsl7TsnbTrpoQg67CU6r646riwIAoKCiMjIOq1rOyEseyalOyGjCDshKDtg53tlZjquLAgKGRyb3A9RiDsmLXshZgg64Sj7Jy866m0IOybkOuemCDrjbDsnbTthLAg7ZSE66CI7J6EIO2Yle2DnOuMgOuhnCDshLjroZzroZwg67CY7ZmYIOuQqCApCiMg7JyE7LmYKOyijO2RnCnsmYAg7J24642x7IqkIO2ZnOyaqSAKZGZfMlsyLF0gIyAy67KI7Ke4IO2WiSAKZGZfMlssMl0gIyAy67KI7Ke4IOyXtApkZl8yWzJdICAjIOyXtOyijO2RnCAtPiBkZiDtmJXtg5zroZwg67CY7ZmYIApkZl8yW1syXV0gIyDsnbjrjbHsiqQgIC0+IOuyoe2EsCDtmJXtg5zroZwg67CY7ZmYIApkZl8yW2MoMTozKSxdCmRmXzJbLGMoMSwyKV0KZGZfMiRjaGEgICMgY2hhIOudvOuKlCDsnbTrpoTsnYQg6rCA7KeEIOyXtCAtPiDrsqHthLAg67CY7ZmYIApkZl8yJGNoYVsyXSAjIGNoYeyXtOydmCAy67KI7Ke4IO2WiQoKZGZfMlstMl0gIyAy67KI7Ke4IOyXtCDsoJzsmbggCmRmXzJbLWMoMTozKSxdICMgMS0zIO2WiSDsoJzsmbggCmRmXzJbLC1jKDIsNCldIAoKZGZfMiRhbHAgPC0gTlVMTCAgIyBOVUxM7J2EIOydtOyaqe2VnCDrs4DsiJgg7KCc6rGwICjrjbDsnbTthLDtlITroIjsnoQg7Je0IOygnOqxsCkKZGZfMgoKCiMg67OA7IiYIOydtOumhCDtmZzsmqkgCiMgMSkg67Kh7YSw66GcIOuwmO2ZmCAKZGZfMiA8LSBkYXRhLmZyYW1lKG51bSxjaGEsbG9naSxhbHA9YygiYSIsImIiLCJjIiwiZCIsImUiKSk7IGRmXzIKCmRmXzJbWyJjaGEiXV0KZGZfMiRjaGEKZGZfMlssImNoYSJdCgojIDIpIOuNsOydtO2EsCDtlITroIjsnoTsnLzroZwg67CY7ZmYICjrjIDqtIDtmLgg7ZWY64KY66eMISkKZGZfMlsiY2hhIl0KZGZfMltjKCJjaGEiLCJhbHAiKV0KZGZfMlssYygiY2hhIiwiYWxwIildCgoKIyMg64W866asIOyXsOyCsOyekAojID09IOqwmeuLpC4gIT0g64uk66W064ukLiA+PSAsIDw9ICwgJiwgfAojIEEgJWluJSAoYygyLDQsIDUpKSAgOiBB64qUIDIsIDQsIDUg7KSRIO2VmOuCmOyduCDqsoMKYGBgCgoKYGBge3IgNeyjvOywqCA6IGlyaXMg642w7J207YSwIH0KIyA16rCcIOuzgOyImCA6IFNlcGFsLkxlbmd0aCjqvYPrsJvsuagg6ri47J20KSwgU2VwYWwuV2lkdGgo6r2C67Cb7Lmo64SI67mEKSwgUGV0YWwuTGVuZ3RoKOq9g+yejuq4uOydtCksIFBldGFsLldpZHRoLCBTcGVjaWVzCgojIyBpcmlzIOuNsOydtO2EsCDsgrTtjrTrs7TquLAgIApoZWFkKGlyaXMpICMg66eoIOyVniA27ZaJIOy2nOugpQpoZWFkKGlyaXMsMTApCnRhaWwoaXJpcykgIyDrp6gg65KkIDbtlokg7Lac66ClCnRhaWwoaXJpcywxMCkKc3RyKGlyaXMpICMg642w7J207YSwIOq1rOyhsCDtmZXsnbggCgptZWFuKGlyaXMkU2VwYWwuTGVuZ3RoKQpzZChpcmlzJFNlcGFsLkxlbmd0aCkKaGlzdChpcmlzJFNlcGFsLkxlbmd0aCkgIyDtnojsiqTthqDqt7jrnqggCgoKIyMg642w7J207YSwIO2UhOugiOyehOyXkOyEnCDsobDqsbTsl5Ag66ee64qUIOq1rOyEseyalOyGjCDshKDtg53tlZjquLAKIyAxLiB3aGljaCjsobDqsbQpIDog7KGw6rG07J2EIOunjOyhse2VmOuKlCDsm5Dshozrk6TsnZgg7JyE7LmYKO2WiSDsnITsuZgp66W8IOy2nOugpSAKd2hpY2goaXJpcyRTZXBhbC5MZW5ndGggPiA2KQppcmlzJFNlcGFsLkxlbmd0aFtjKDUxLDUyLDUzLDU0KV0gICPtmZXsnbgKaXJpc1t3aGljaChpcmlzJFNlcGFsLkxlbmd0aCA+IDYpLCJTcGVjaWVzIl0gICMg6r2D67Cb7LmoIOq4uOydtOqwgCA267O064ukIO2BsCDsooUgCmlyaXNbd2hpY2goaXJpcyRTZXBhbC5MZW5ndGg8PTYgJiBpcmlzJFNlcGFsLldpZHRoPT0zKSxdICAjIO2VtOuLuSDsobDqsbTsnYQg66eM7KGx7ZWY64qUIOq0gOy4oey5mCAKaXJpc1t3aGljaChpcmlzJFNlcGFsLkxlbmd0aDw9NiAmIGlyaXMkU2VwYWwuV2lkdGg9PTMpLCJTcGVjaWVzIl0gICMg7ZW064u5IOyhsOqxtOydhCDrp4zsobHtlZjripQg7KKFIAppcmlzW3doaWNoKGlyaXMkUGV0YWwuTGVuZ3RoPjEuNSB8IGlyaXMkUGV0YWwuV2lkdGg8MC4zKSwiU3BlY2llcyJdCgojIDIuIHN1YnNldChkZiwgc3Vic2V0PSjsobDqsbQpLCBzZWxlY3Q97Je07J2066aELCBkcm9wPUYpIC0+IGRmIO2Yle2DnOuhnCDrsJjtmZggCiMg7KGw6rG07J2EIOunjOyhse2VmOuKlCBzZWxlY3Tsl7Qo65Sw66GcIOyngOyglSDslYjtlZjrqbQg66qo65OgIOyXtCnsnZgg6rCS65OkLiBkcm9wPUbrqbQgZGbroZwg7Lac66ClIFTrqbQg67Kh7YSw66GcIOy2nOugpSAKc3Vic2V0KGlyaXMsc3Vic2V0PShpcmlzJFNlcGFsLkxlbmd0aCA+IDcpLCBzZWxlY3Q9U3BlY2llcykKaXJpc1t3aGljaChpcmlzJFNlcGFsLkxlbmd0aCA+IDcpLGMoIlNlcGFsLkxlbmd0aCIsIlNwZWNpZXMiKV0KCnN1YnNldChpcmlzLHNlbGVjdD1jKCJQZXRhbC5MZW5ndGgiLCJTcGVjaWVzIiksc3Vic2V0PShpcmlzJFBldGFsLkxlbmd0aDw9My41ICYgaXJpcyRTcGVjaWVzIT0ic2V0b3NhIikpCnN1YnNldChpcmlzLHN1YnNldD0oaXJpcyRTZXBhbC5MZW5ndGg8NS41ICYgaXJpcyRTcGVjaWVzICVpbiUgKGMoInZlcnNpY29sb3IiLCJ2aXJnaW5pY2EiKSkpKQoKCgojIyDsl7DsirXrrLjsoJwgCiMxKSAKaXJpc1t3aGljaChpcmlzJFBldGFsLkxlbmd0aCA+IDYpLGMoIlNlcGFsLkxlbmd0aCIsIlNwZWNpZXMiKV0KIzIpIApzdWJzZXQoaXJpcyxzZWxlY3Q9YygiU2VwYWwuTGVuZ3RoIiwiU3BlY2llcyIpLHN1YnNldD0oaXJpcyRQZXRhbC5MZW5ndGggPiA2KSkKIzMpIOu2k+q9gyDsooXrs4Qg6r2D7J6OIOq4uOydtOydmCDtj4nqt6DsnYQg6rWs7ZWY7Iuc7JikIAp0YXBwbHkoaXJpcyRQZXRhbC5MZW5ndGgsIGlyaXMkU3BlY2llcywgbWVhbikKIyA0KSDqvYPrsJvsuagg6ri47J206rCAIDXsnbTtlZjsnbTqs6AsIOu2k+q9g+ydmCDsooXsnbQgc2V0b3NhIOyduCDqvYbsno4g6ri47J207J2YIO2Pieq3oOydhCDqtaztlZjsi5zsmKQgCm1lYW4oaXJpc1t3aGljaChpcmlzJFNlcGFsLkxlbmd0aDw9NSAmIGlyaXMkU3BlY2llcz09InNldG9zYSIpLCJQZXRhbC5MZW5ndGgiXSkgIyDqsJLrk6TsnbQg67Kh7YSwIO2Yle2DnOuhnCDrgpjsmKTri4jquYwgbWVhbigpCmFwcGx5KHN1YnNldChpcmlzLHNlbGVjdD0iUGV0YWwuTGVuZ3RoIixzdWJzZXQ9KGlyaXMkU2VwYWwuTGVuZ3RoPD01ICYgaXJpcyRTcGVjaWVzPT0ic2V0b3NhIikpLDIsbWVhbikgIyBkZiDtmJXtg5zri4jquYwgYXBwbHkKCgoKYGBgCgpgYGB7ciA27KO87LCoIDogZGYg67OA7IiYIOy2lOqwgOyZgCBtZXJnZX0Kcm0obGlzdD1scygpKQoKIyMg642w7J207YSwIO2UhOugiOyehCDrs4DsiJgg7LaU6rCA7ZWY6riwIAojIDEpIGRmJOyXtOydtOumhApkZl9wIDwtIGRhdGEuZnJhbWUoeDE9YygxLDIsMyw0KSx4Mj1jKDIsNCw2LDgpKTsgZGZfcAp4MTIgPC0gZGZfcCR4MSArIGRmX3AkeDI7IHgxMgpkZl9wJHgzIDwtIGRmX3AkeDEgKyBkZl9wJHgyOyBkZl9wCmRmX3AkeDIyIDwtIGRmX3AkeDIgLyBkZl9wJHgxOyBkZl9wCgojIDIpIGRmWyzsl7TrsojtmLhdIApkZl9wWyw1XSA8LSBkZl9wWywxXSArIGRmX3BbLDNdCmRmX3AgICPrs4DsiJjrqoXsnbQgVjXroZwg7ISk7KCVKOyekOuPmeycvOuhnH4pCgojIDMpIGRmX25ldyA9IHRyYW5zZm9ybShkZiwg7Je07J2066aEPWMoKSkKZGZfcCA8LSB0cmFuc2Zvcm0oZGZfcCx4MT1jKDAsMCwwLDApKTtkZl9wICAj67OA7IiYIOyImOyglQpkZl9wIDwtIHRyYW5zZm9ybShkZl9wLHg1PXgyK3gzLG1lYW54MTI9KHgxK3gyKS8yKTtkZl9wICAj67OA7IiYIOy2lOqwgAoKIyBkZl9wIDwtIHRyYW5zZm9ybShkZl9wLCBtZWFueDEyPU5VTEwpO2RmX3AgIyDrs4DsiJgg7KCc6rGwCiMgZGZfcCRtZWFueDEyIDwtTlVMTDtkZl9wICMg67OA7IiYIOygnOqxsAoKCiMjIOusuOyekO2YlSDrs4DsiJjrpbwg67KU7KO87ZiVIOuzgOyImChmYWN0b3IsIOyalOyduCnroZwg67OA7ZmYIApkZl9wJHRlbltkZl9wJFY1PDEwXSA8LSAiZG93biIKZGZfcCR0ZW5bZGZfcCRWNT49MTBdIDwtICJ1cCIKY2xhc3MoZGZfcCR0ZW4pICPrrLjsnpDtmJUKCmRmX3AkdGVuIDwtIGZhY3RvcihkZl9wJHRlbik7IGRmX3AkdGVuICPrspTso7woZmFjdG9yKe2YleycvOuhnCDrs4DtmZggCmNsYXNzKGRmX3AkdGVuKQpzdHIoZGZfcCkKCgojIyDrjbDsnbTthLAg7ZSE66CI7J6E7J2YIOqysO2VqSAKIyAxKSByYmluZCgpLCBjYmluZCgpIDog7JyE7JWE656Y66GcIOqysO2VqSjrs4DsiJjrqoXsnbQg7J287LmY7ZW07JW8IO2VqCEhKSwg7KKM7Jqw66GcIOqysO2VqSAKcmJpbmQoZGZfcixkZl9vKQpjYmluZChkZl9yLGRmX28pCgoKIyAyKSBtZXJnZSh4LCB5LCBieSA9IOq4sOykgOuzgOyImCwgYnkueD0sIGJ5LnkgPSwgYWxsPUYsIGFsbC54PUYsIGFsbC55PUYpCm1lcmdlKGRmXzEsIGRmXzIsIGJ5ID0gIm5hbWUiKSAjaW5uZXIgam9pbi4g6rWQ7KeR7ZWpIAptZXJnZShkZl8xLCBkZl8yLCBieSA9ICJuYW1lIiwgYWxsPVQpICNvdXRlciBqb2luLiDtlansp5HtlakKCm1lcmdlKGRmXzEsIGRmXzIsIGJ5PSAibmFtZSIsIGFsbC54PVQpICNsZWZ0IG91dGVyIGpvaW4uIOyyq+uyiOynuCBkZiDqsJIg7Jyg7KeAIAptZXJnZShkZl8xLCBkZl8yLCBieT0gIm5hbWUiLCBhbGwueT1UKSAjcmlnaHQgb3V0ZXIgam9pbgptZXJnZShkZl8xLCBkZl8yLCBieT0gIm5hbWUiLCBhbGwueD1ULCBhbGwueT1UKSAjb3V0ZXIgam9pbgoKbWVyZ2UoZGZfMSwgZGZfMywgYnkueD0gIm5hbWUiLCBieS55PSAic3R1ZGVudCIsIGFsbD1UKSAjIOq4sOykgCDrs4DsiJgg7J2066aE7J20IOuLpOulvOuVjCEg6rCB6rCBIOq4sOykgOuzgOyImCDsnbTrpoQg7KeA7KCVLgpgYGAKCgpgYGB7ciA27KO87LCoIDog7YyM7J28IOu2iOufrOyYpOq4sCB9CiMjIOuNsOydtO2EsOyFiyDrtojrn6zsmKTquLAgOiBvcHRpb24g7YKkIOuIhOultOuptCDqsr3roZzrs7Xsgqwg66mU64m0IOuztOyehCEhIAojIHR4dCDtmJXsi50gOiByZWFkLnRhYmxlKCLqsr3roZwvZmlsZSIsIGhlYWRlcj1GLCBzZXA9IiIsIC4uLikKIyBjc3Yg7ZiV7IudIDogcmVhZC5jc3YoIuqyveuhnC9maWxlIiwgaGVhZGVyPUYsIHNlcD0iLCIsIC4uLikKIyBoZWFkZXIgOiDrs4DsiJjrqoUg7Jes67aALCBuYS5zdHJpbmdzIDog6rKw7Lih6rCSIE5BIO2RnOyLnCAKc2NvcmUxIDwtIHJlYWQudGFibGUoIi9Vc2Vycy9qYXkvRGVza3RvcC9kYXRhL3Njb3JlMS50eHQiLHNlcD0iOyIsbmEuc3RyaW5ncz0iOTk5OSIpICMgOTk5OeudvOuKlCDqsJLsnYQgTkEg7LKY66asCnNjb3JlMiA8LSByZWFkLmNzdigiL1VzZXJzL2pheS9EZXNrdG9wL2RhdGEvc2NvcmUyLmNzdiIpCgojIyDrgrTrs7TrgrTquLAgCiMgd3JpdGUudGFibGUoZGF0YSwgZmlsZT0i6rK966GcIiwgc2VwPSIgIiwgcXVvdGU9VCwgcm93Lm5hbWVzPVQsIGNvbC5uYW1lcz1ULCAuLi4pCiMgd3JpdGUuY3N2KGRhdGEsIGZpbGU9IuqyveuhnCIsIC4uLikKY29sbmFtZXMoc2NvcmUxKSA8LSBjKCJOQU1FIiwiQUdFIiwiTUlEVEVSTSIpO3Njb3JlMQpzY29yZSA8LSBtZXJnZShzY29yZTEsc2NvcmUyLGJ5PWMoIk5BTUUiLCJBR0UiKSxhbGw9VCk7IHNjb3JlCgp3cml0ZS50YWJsZShzY29yZSwiL1VzZXJzL2pheS9EZXNrdG9wL2RhdGEvc2NvcmVxX3QudHh0IixzZXA9Il8iLHF1b3RlPUYpICMgcXVvdGU9RiA6IOqwgSDqsJLrk6TsnYQg65Sw7Ji07ZGc66GcIOustuyngCDrp5DslYTrnbwuCndyaXRlLmNzdihzY29yZSwiL1VzZXJzL2pheS9EZXNrdG9wL2RhdGEvc2NvcmVyX2MuY3N2Iixyb3cubmFtZXM9RikgIyDtlokg7J2066aE7J2EIO2MjOydvOyXkCDtj6ztlajtlaDsp4Ag7Jes67aACgoKIyMg7Jew7Iq166y47KCcIAojMQpzY29yZTMgPC0gcmVhZC50YWJsZSgiL1VzZXJzL2pheS9EZXNrdG9wL2RhdGEvc2NvcmUzLnR4dCIsc2VwPSJcdCIpO3Njb3JlMwojMgpzY29yZTQgPC0gZGF0YS5mcmFtZShOQU1FID0gYygiSlVZIiwiS0pZIiwiS0dIIiksIEFHRSA9IGMoMjYsMjMsMjIpLCBGSU5BTCA9IGMoNDUsMjQsODMpKTtzY29yZTQKIzMKY29sbmFtZXMoc2NvcmUzKSA8LSBjKCJOQU1FIiwiQUdFIiwiTUlEVEVSTSIpO3Njb3JlMwojNApzY29yZTUgPC0gbWVyZ2Uoc2NvcmUzLHNjb3JlNCxieT1jKCJOQU1FIiwgIkFHRSIpLCBhbGw9VCk7c2NvcmU1CiM1CndyaXRlLmNzdihzY29yZTUsIi9Vc2Vycy9qYXkvRGVza3RvcC9kYXRhL3Njb3JlNS5jc3YiLHJvdy5uYW1lcz1GKQoKYGBgCgpgYGB7ciA37KO87LCoIOuNsOydtO2EsOyFiyDri6TsmrTroZzrk5zsmYAgZGYg7ZWo7IiY7KCB7JqpfQpybShsaXN0PWxzKCkpCgojIyDrjbDsnbTthLAg64uk7Jq066Gc65OcIAppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoZ2dwbG90MikKCiMjIENhcnM5MyDrjbDsnbTthLAg7YyM7JWFIApsaWJyYXJ5KCJNQVNTIikgIyBS7JeQIOyeiOuKlCDquLDrs7gg7Yyo7YKk7KeA66GcIGluc3RhbGwucGFja2FnZXMg6rO87KCV7J20IO2VhOyalCDsl4bsnYwKaGVscChDYXJzOTMpCnN0cihDYXJzOTMpICAKaGVhZChDYXJzOTMpCnRhaWwoQ2FyczkzKQpzdW1tYXJ5KENhcnM5MykgIyDqsIEg642w7J207YSw7JeQIOuMgO2VnCDsmpTslb3rkJwg7KCV67O0IOy2nOugpSAKCiMjIOqysOy4oeqwkiDsoJzqsbDtlZjquLAgCiMgYW55TkEoKSBOQeqwkiDsnKDrrLQsIGlzLm5hKCkgTkHqsJIg7Jyg66y0IC0+IOuRmOuLpCBOQSDrv5Drp4zslYTri4jrnbwgTmFO64+EIFTroZwg67CY7ZmYISAKIyBuYS5vbWl0KCkg6rKw7Lih6rCS7J20IOyeiOuKlCDqtIDsuKHsuZgg7KCc6rGwKO2WiSDsgq3soJwpICAKQUEgPC0gYygxLDIsTkEsTmFOLDQ1KTsgQUEKYW55TkEoQUEpCmlzLm5hKEFBKQpBPC1kYXRhLmZyYW1lKEFBKTsgQQpuYS5vbWl0KEEpCgpDYXIgPC0gQ2FyczkzCm5yb3coQ2FyKSAjIDkzCmNvbFN1bXMoaXMubmEoQ2FyKSkKQ2FyW3doaWNoKGlzLm5hKENhciRMdWdnYWdlLnJvb20pKSxjKCJNb2RlbCIsIlJlYXIuc2VhdC5yb29tIiwiTHVnZ2FnZS5yb29tIildICMgbHVnZ2FnZS5yb29tIOyXkCDqsrDsuKHsuZjqsIAg7J6I64qUIOq0gOy4oey5mCDtmZXsnbggCgpDYXIgPC0gbmEub21pdChDYXIpCm5yb3coQ2FyKSAjIDgyCmNvbFN1bXMoaXMubmEoQ2FyKSkgIyDqsrDsuKHsuZgg7J6I64qU7KeAIO2ZleyduCAKCgojIyDrjbDsnbTthLAg7ZSE66CI7J6EIOuCtCDrs4DsiJgg7J207Jqp7ZWY6riwIAojIGVnKSDtkZzspIDtmZQgCiMgMSkg642w7J207YSw66qFJOuzgOyImOuqhSAKel9wIDwtIChDYXIkUHJpY2UgLSBtZWFuKENhciRQcmljZSkpL3NkKENhciRQcmljZSkKel9wCm1lYW4oel9wKSAjIO2Pieq3oCAwCnNkKHpfcCkgIyDtkZzspIDtjrjssKggMQoKIyBhdHRhdGNoKOuNsOydtO2EsOuqhSkgOiDrjbDsnbTthLDrqoUk67OA7IiY66qFIOycvOuhnCDrp6Trsogg7JOw6riwIOq3gOywruydhCDrlYwg7JOw66m0IOyekOuPmeycvOuhnCDrtpnsl6zspIwgCmF0dGFjaChDYXIpICAKel9wMSA8LSAoUHJpY2UgLSBtZWFuKFByaWNlKSkvc2QoUHJpY2UpCmRldGFjaChDYXIpICMg64uk7IucIOuWvOq4sCAKel9wMiA8LSAoUHJpY2UgLSBtZWFuKFByaWNlKSkvc2QoUHJpY2UpICAjIOyYpOulmCAKCiMgMikgd2l0aCjrjbDsnbTthLDrqoUsIO2RnO2YhOyLnSkKel9wMiA8LSB3aXRoKENhciwoUHJpY2UgLSBtZWFuKFByaWNlKSkvc2QoUHJpY2UpKSAjIHdpdGgo642w7J207YSwLCDtkZztmITsi50pCnpfcDIKCgojIyDrjbDsnbTthLDtlITroIjsnoTsl5Ag7ZWo7IiYIOyggeyaqe2VmOq4sAojIDEpIGFnZ3JlZ2F0ZSjtlajsiJjsoIHsmqnrs4DsiJjrqoV+6re466O567aE66WY7ZWg67OA7IiY66qFLCDrjbDsnbTthLAsIO2VqOyImCkKP2FnZ3JlZ2F0ZSAjIOuNsOydtO2EsOulvCDqt7jro7nrs4TroZwg7JqU7JW97ZWgIOuVjCDsgqzsmqkKYWdncmVnYXRlKFByaWNlfk1hbnVmYWN0dXJlcixDYXIsbWVhbikgICMg7KCc7KGw7IKs67OEIHByaWNlIO2Pieq3oCAKYWdncmVnYXRlKEVuZ2luZVNpemV+TWFudWZhY3R1cmVyK1R5cGUsQ2FyLHN1bSkgIyDsoJzsobDsgqzrs4QsIHR5cGXrs4Qg7JeU7KeE7YGs6riwIO2VqSAKCiMgMikgYXBwbHkoeCwgbWFyZ2luLCDtlajsiJgpIDogCiMgeCA6IOuwsOyXtCDtmLnsnYAg7ZaJ66CsCiMgbWFyZ2luIDogMShyb3dzLCDqsJnsnYAg7ZaJ64G866asIO2VqOyImCDsoIHsmqkpLCAyKGNvbHVtbnMpCj9hcHBseQpDYXJfcCA8LUNhcltjKDIsNDo2KV07IENhcl9wIAphcHBseShDYXJfcFsyOjRdLDEsc2QpICAjIOqwmeydgCDtloksIO2VmOuCmOydmCDqtIDsuKHsuZgg67OEIDItNOuyiOynuCDrs4DsiJjqsJLrk6TsnZgg7ZGc7KSA7Y647LCoIAphcHBseShDYXJfcFsyOjRdLDIsbWVhbikgICMg6rCZ7J2AIOyXtCgyLTTrsojsp7gg7Je0KSwg7ZWY64KY7J2YIOuzgOyImCDrs4Qg7Y+J6regIAoKIyAzKSB0YXBwbHko67Kh7YSwLCDsp5Hri6jrtoTrpZjtlaDrs4DsiJgsIO2VqOyImCwgLi4uKSA6IHRhYmxlIGFwcGx5LiDsmpTsnbjsnZggbGV2ZWzsl5Ag65Sw6528IO2VtOuLueuQmOuKlCjqsJnsnYAg7JyE7LmY7J2YKSDrsqHthLDrpbwg66y26rOgIO2VqOyImOulvCDsoIHsmqkgCj90YXBwbHkKdGFwcGx5KENhciRQcmljZSwgQ2FyJE1hbnVmYWN0dXJlciwgbWVhbikgCgoKIyMg642w7J207YSwIO2UhOugiOyehCDrtoTrpqztlZjquLAgCiMgc3BsaXQoeCwgZiwgZHJvcD1GLCAuLi4pIDog7JqU7J24KGYp7J2YIGxldmVs7JeQIOuUsOudvCDrjbDsnbTthLDtlITroIjsnoQo7Zi57J2AIOuyoe2EsCwgeCnsnYQg67aE7ZW07ZWoIC0+IGxpc3Qg67CY7ZmYICAKP3NwbGl0CmFpcl9wIDwtIHNwbGl0KENhciRQcmljZSxDYXIkQWlyQmFncyk7IGFpcl9wICAKc3RyKGFpcl9wKQptZWFuKGFpcl9wW1sxXV0pICAjRHJpdmVyJlBhc3NlbmdlcgpzdW0oYWlyX3BbWzJdXSkgICNEcml2ZXIgb25seQpzZChhaXJfcFtbM11dKSAgI05vbmUKYGBgCgojIOuwsOyXtChhcnJheSkKYGBge3IgN+yjvOywqCBhcnJheX0KIyBhcnJheShkYXRhLCBkaW09YyhhMSxhMixhMywuLi4pLCBkaW1uYW1lcz1saXN0KCksLi4uKQphcnIxIDwtIGFycmF5KDE6OSk7IGFycjEKYXJyMiA8LSBhcnJheShMRVRURVJTLGRpbT1jKDIsMTMpKTsgYXJyMgphcnIzIDwtIGFycmF5KG1vbnRoLmFiYixkaW09YygyLDIsMykpOyBhcnIzICMgM+ywqOybkCAKYXJyNCA8LSBhcnJheShzZXEoMiwzMiwyKSxkaW09YygyLDIsMiwyKSk7IGFycjQgIyA07LCo7JuQLi4uIOuKmOumrOuKlOuMgOuhnCDqs4Tsho0g64qY66a0IOyImCDsnojsnYwKCmFyciA8LSBhcnJheShjKDE6MTIpLGRpbT1jKDMsMiwyKSxkaW1uYW1lcz1saXN0KCLtlokiPXBhc3RlKCLtlokiLGMoMTozKSksIuyXtCI9cGFzdGUoIuyXtCIsYygxOjIpKSwi66m0Ij1wYXN0ZSgi66m0IixjKDE6MikpKSkKCmRpbW5hbWVzKGFycjMpIDwtIGxpc3QocGFzdGUwKCLtlokiLGMoMSwyKSkscGFzdGUwKCLsl7QiLGMoMSwyKSkscGFzdGUwKCLrqbQiLGMoMTozKSkpIApkaW0oYXJyMykgIyDssKjsm5Ag7ZmV7J24IAoKIyMg67Cw7Je07J2YIOyXsOyCsCA6IOqwmeydgCDsnITsuZjsnZgg7JuQ7IaM6rCSIOuBvOumrCDsl7DsgrDtlago6rCZ7J2AIO2BrOq4sOydmCDrsLDsl7Qg7J207Ja07JW8IOyCrOy5meyXsOyCsCDsiJjtlonqsIDriqUhKQphcnIueCthcnIueSAgI+uNlO2VmOq4sAphcnIueC1hcnIueSAgI+u5vOq4sAphcnIueCphcnIueSAgI+qzse2VmOq4sAphcnIueC9hcnIueSAgI+uCmOuIhOq4sAphcnIueF5hcnIueSAgI27soJzqs7EKYXJyLnglJWFyci55ICAj64KY66i47KeACgphcnIueCUqJWFyci55ICMgJSolIDog7ZaJ66Cs7J2YIOqyveyasCAtPiDtlonroKzqs7EgLCDrsLDsl7TsnZgg6rK97JqwICrsnZgg6rKw6rO866W8IO2Vqe2VnCDqsoPqs7wg6rCZ7J2MIApzdW0oYXJyLngqYXJyLnkpCgoKIyMg67Cw7Je07JeQ7ISc7J2YIOuNsOydtO2EsCDqsoDsg4kgLT4g7ZaJ66Cs6rO8IOu5hOyKtyAKIyBhcnJb7ZaJLOyXtCzrqbQo7LCo7JuQKV0KYXJyLnggPC0gYXJyYXkoMToxOCxkaW09YygzLDMsMikpOyBhcnIueAphcnIueFsxXSAgCmFyci54WzE4XSAjIDE467KI7Ke4IOuNsOydtO2EsCAKYXJyLnhbMSwgLCBdCmFyci54WyAsMSwgXQphcnIueFsgLCAsIDFdIAphcnIueFsgLCAsIDJdCgphcnIueFsxLDEsIF0gIyDrqqjrk6Ag66m07J2YIDHtlokx7Je07J2YIOqwkiAKYXJyLnhbMiwyLCBdIAphcnIueFsyLCAsMV0gCmFyci54WyAsMywyXQoKYXJyLnhbMSwtMSwgXSAjIDHsl7TsnYQg7KCc7Jm47ZWcLCDrqqjrk6Ag66m07J2YIDHtlokgCmFyci54Wy0zLCAsIDJdCgptZWFuKGFyci54WzEsICwxXSkgICMgMeuptOydmCAx7ZaJIOuNsOydtO2EsOydmCDtj4nqt6DqsJIgCgojIyDsl7DsirXrrLjsoJwKIygxKSBpcmlzIOuNsOydtO2EsOydmCDqsIEg67OA7IiY67OEIOqysOy4oey5mOqwgCDsnojripTsp4Ag7ZmV7J247ZWY6riwCmNvbFN1bXMoaXMubmEoaXJpcykpCgojKDIpIGFnZ3JlZ2F0ZSDtlajsiJjrpbwg7IKs7Jqp7ZW07IScIGlyaXMg642w7J207YSw7J2YIFNwZWNpZXPrs4QgU2VwYWwuTGVuZ3Ro7J2YIO2Pieq3oOqzvCDstJ3tlakg6rWs7ZWY6riwCmFnZ3JlZ2F0ZShTZXBhbC5MZW5ndGh+U3BlY2llcywgaXJpcywgbWVhbikKYWdncmVnYXRlKFNlcGFsLkxlbmd0aH5TcGVjaWVzLCBpcmlzLCBzdW0pCgojKDMpIHRhcHBseSDtlajsiJjrpbwg7IKs7Jqp7ZW07IScIGlyaXMg642w7J207YSw7J2YIFNwZWNpZXPrs4QgU2VwYWwuTGVuZ3Ro7J2YIO2Pieq3oOqzvCDstJ3tlakg6rWs7ZWY6riwCiMjIOuwqeuylSAxCnRhcHBseShpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRTcGVjaWVzLCBtZWFuKQp0YXBwbHkoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU3BlY2llcywgc3VtKQoKIyMg67Cp67KVIDIKYXR0YWNoKGlyaXMpCnRhcHBseShTZXBhbC5MZW5ndGgsIFNwZWNpZXMsIG1lYW4pCnRhcHBseShTZXBhbC5MZW5ndGgsIFNwZWNpZXMsIHN1bSkKCmBgYAojIOumrOyKpO2KuChsaXN0KQpgYGB7ciA47KO87LCoIOumrOyKpO2KuH0Kcm0obGlzdD1scygpKQoKIyBsaXN0KCkg7J207JqpIApvYmogPC0gbGlzdCgpICPruYgg66as7Iqk7Yq4IOqwneyytCDsg53shLEKb2JqW1sxXV0gPC0gInVzZXIxIiAjIOyyqyDrsojsp7gg66as7Iqk7Yq4IOqwneyytCDsoIDsnqUKb2JqW1syXV0gPC0gMjAgIyDrkZAg67KI7Ke4IOumrOyKpO2KuCDqsJ3ssrQg7KCA7J6lCm9ialtbM11dIDwtIEYgIyDshLgg67KI7Ke4IOumrOyKpO2KuCDqsJ3ssrQg7KCA7J6lCm9ialtbNF1dIDwtIGMoNzAsIDgwLCA5MCwgMTAwKSAjIOuEpCDrsojsp7gg66as7Iqk7Yq4IOqwneyytCDsoIDsnqUKb2JqCgojIOumrOyKpO2KuCDsg53shLEg67CPIOu2hO2VtCAKbGlzdC51c2VyIDwtIGxpc3QoInVzZXIxIiwgMjMsICJNIiwgYyg4MCw4NSw5MCw5NSkpOyBsaXN0LnVzZXIKdW5saXN0KGxpc3QudXNlcikgICMg67Kh7YSw6rCS7J2EIOOFl+u2hO2VtCAKbGlzdC51c2VyMiA8LSBsaXN0KElEID0gInVzZXIxIiwgYWdlID0gMjMsIGdlbmRlciA9ICJNIiwgZ3JhZGUgPSBjKDgwLCA4NSwgOTAsIDg1KSk7IGxpc3QudXNlcjIKCiMg66as7Iqk7Yq4IO2VreuqqeyXkCDsnbTrpoQg7KCB7JqpIApsaXN0LnVzZXIyIDwtIGxpc3QoSUQgPSAidXNlcjEiLCBhZ2UgPSAyMywgZ2VuZGVyID0gIk0iLCBncmFkZSA9IGMoODAsIDg1LCA5MCwgODUpKTsgbGlzdC51c2VyMgpuYW1lcyhsaXN0LnVzZXIyKQpuYW1lcyhsaXN0LnVzZXIyKSA8LSBjKCJVU0VSIiwgIkFHRSIsICJHRU5ERVIiLCAiR1JBREUiKSAjIOydtOumhCDsiJjsoJUgCgojIOuLpOyWke2VnCDsnKDtmJXsnZgg642w7J207YSw66W8IO2VmOuCmOydmCDrpqzsiqTtirjsl5Ag64ukIOuEo+ydhCDsiJgg7J6I7J2MISEgCmxpc3QuZXggPC0gbGlzdCgiSG9uZyIsIGMoMToxMCksIGMoVCxGKSkKYWxsX2xpc3QgPC0gbGlzdChjKDE6NSksIG1hdHJpeCgxOjksIDMsMyksIGxpc3QuZXgpOyBhbGxfbGlzdAoKYWxsX2xpc3QgPC0gbGlzdChjKDE6NSksIG1hdHJpeCgxOjksIDMsMyksIGxpc3QuZXgpOyBhbGxfbGlzdAphbGxfbGlzdFtbMV1dIDwtIE5VTEwgIyDrpqzsiqTtirgg6rCd7LK0IOygnOqxsCAKYWxsX2xpc3QKCiMjIOumrOyKpO2KuCDqsJ3ssrQg67OA6rK9IAphbGxfbGlzdCA8LSBsaXN0KGMoMTo1KSwgbWF0cml4KDE6OSwgMywzKSwgbGlzdC5leCk7IGFsbF9saXN0CmFsbF9saXN0W1sxXV1bNV0gPC0gMTAgICAjIOyyq+uyiOynuCDtla3rqqnsnZggNeuyiOynuCDqsJLsnYQgMTDsnLzroZwg67OA6rK9IAphbGxfbGlzdFtbMl1dIDwtIG1hdHJpeCgxOjEyLDMsNCkKYWxsX2xpc3RbWzJdXSA8LSBhbGxfbGlzdFtbMl1dWy0zLF0gICMgMuuyiOynuCDtla3rqqnsnZggM+uyiOynuCDtlonsnYQg7KCc7Jm47ZWcIOuqqOuToCDqsJIgCmFsbF9saXN0CmBgYAoKIyBmdW5jdGlvbigpCmBgYHtyIDjso7zssKggZnVuY3Rpb24oKe2VqOyImCB9Cm15cG93ZXIwMiA8LSBmdW5jdGlvbih4LHkpewogIHByaW50KHheeSkKfQpteXBvd2VyMDIoMywyKQpteXBvd2VyMDIoMywzKQpgYGAKCiMgaWbrrLggZm9y66y4IHdoaWxl66y4CmBgYHtyIDggOeyjvOywqCBpZuusuCBmb3LrrLggd2hpbGXrrLggfQoKIyMjIyMjIyMjIGlmIOusuCAjIyMjIyMjIyMjCiMgaWYo7KGw6rG0KXsKIyDssLjsnbzrlYwg7Iuk7ZaJ7ZWgIOqygwojIH1lbHNlewojIOqxsOynk+ydvOuVjCDsi6TtlontlaAg6rKDCiMgfQoKeDwtIC0zCmlmICh4IDwgMCl7CiAgcHJpbnQoLXgpCn0gZWxzZXsKICBwcmludCh4KQp9CgpteWFicyA8LSBmdW5jdGlvbih4KXsKICBpZiAoeCA8IDApewogICAgcHJpbnQoLXgpCiAgfSBlbHNlewogICAgcHJpbnQoeCl9Cn0KbXlhYnMoLTIpOwoKCiMg7KGw6rG07J20IDPqsJwg7J207IOB7J24IOqyveyasCBpZiAtIGVsc2UgaWYgLSBlbHNlCng8LTEwCmlmICh4PjE1KSB7CiAgcHJpbnQoInjqsIAgMTXrs7Tri6Qg7YGs64ukIikKfSBlbHNlIGlmICh4PjUpIHsKICBwcmludCgieOqwgCA167O064ukIO2BrOuLpCIpCn0gZWxzZSB7CiAgcHJpbnQoInjqsIAgNeydtO2VmOydtOuLpCIpCn0KCiMjIGlmZWxzZSjsobDqsbQsIOywuOydvOuVjCDqsJIsIOqxsOynk+ydvOuVjCDqsJIpCnggPC0gMToxMAppZmVsc2UoeCAlJSAyID09IDAsICLsp53siJgiLCAi7ZmA7IiYIikKCgoKIyMjIyMjIyMjIyMjIyBmb3Ig66y4ICMjIyMjIyMjIyMjIyMjCiMgZm9yKOuzgOyImCBpbiDrsJjrs7XqtazqsIQpeyDrsJjrs7XtlaAg7IudfQpmb3IoaSBpbiAxOjUpewogIHByaW50KGkpCn0KCmZvciAoaSBpbiBjKDMsNCwyLDEpKSB7CiAgcHJpbnQoaSkKfQoKCm15c3VtIDwtIGZ1bmN0aW9uKG4pIHsKICBhIDwtIDAKICBmb3IgKGkgaW4gMTpuKSB7CiAgICBhIDwtIGEraQogIH0KICBwcmludChhKQp9CgojIyBicmVhayA6IOuwmOuzteusuCDsmYTsoITtnogg7KKF66OMCnggPC0gMToxMApmb3IoaSBpbiB4KXsKICBpZihpPT01KXsKICAgIGJyZWFrfSAgIyA16rCAIOuCmOyYpOuptCDsooXro4wKICBwcmludChpKX0KCiMjIG5leHQgOiDtmITsnqwg67CY67O17J2AIOqxtOuEiOubsOqzoCDri6TsnYzsnLzroZx+CnggPC0gMToxMApmb3IoaSBpbiB4KXsKICBpZihpPT01KXsKICAgIG5leHR9ICMgNeqwgCDrgpjsmKTrqbQg64uk7J2M7Jy866GcCiAgcHJpbnQoaSl9CgoKIyBlZykg6rWs6rWs64uoCm4gPC0gMTo5OyBtIDwtIDE6OQpmb3IoIGkgaW4gbSkgewogIGZvciggaiBpbiBuKXsKICAgIHJlc3VsdCA8LSBpKmo7IHByaW50KHBhc3RlKGksICAiKiIsIGosICI9IiwgcmVzdWx0KSl9Cn0KCiMgMuywqOybkCDtlonroKwg6rWs7KGw66GcIOq1rOq1rOuLqCAKTV90IDwtIG1hdHJpeCgwLCBucm93PTksIG5jb2w9OSk7IE1fdApmb3IoaSBpbiAxOjkpewogIGZvcihqIGluIDE6OSl7CiAgICBNX3RbaSxqXSA8LSBpKmp9fQpwcmludChNX3QpCgoKCiMgZm9yKCkg67CY67O166y47J2EIO2Gte2VtCDqtazqtazri6gg66eM65Ok6riwCmZvciAoaSBpbiAxOjkpIHsKICBmb3IgKGogaW4gMTo5KSB7CiAgICBwcmludChwYXN0ZTAoaSwiWCIsaiwiPSIsaSpqKSkKICB9Cn0KCgoKIyMjIyMjIyMjIyMjIyMjIyMjIHdoaWxlIOusuCAjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHdoaWxlKOyhsOqxtCl767CY67O17ZWg7IudfSA6IOyhsOqxtOydtCDssLjsnbTrqbQg6rOE7IaNIOyImO2WiQp4PC0wCnk8LTAKd2hpbGUoeTw9MTApewogIHg8LXgreQogIHk8LXkrMQogIHByaW50KHBhc3RlMCgieD0iLHgsICIgeT0iLHkpKQp9Cng7eQoKCiMg7ZS867O064KY7LmY7IiY7Je0IApmaWI8LTAKbGFzdF9maWI8LTEKd2hpbGUobGFzdF9maWI8MzAwKXsKICBmaWI8LWMoZmliLGxhc3RfZmliKQogIGxhc3RfZmliPC1maWJbbGVuZ3RoKGZpYiktMV0rZmliW2xlbmd0aChmaWIpXQp9CmZpYgoKCgojIDHsl5DshJwgMTAw6rmM7KeA7J2YIO2VqSDqtaztlZjquLAgCmEgPC0gMTsgcmVzdWx0IDwtIDAKd2hpbGUoYTw9MTAwKXsKcmVzdWx0IDwtIHJlc3VsdCArYQphIDwtIGErMX0KcHJpbnQocGFzdGUoIuy0ne2VqToiLCByZXN1bHQpKQoKCiMg6rWs6rWs64uoIDLri6gg7Lac66ClIAp4PC0yOyB5PC0yCndoaWxlKHk8PTEwKXsKICByZXN1bHQgPC0geCp5CiAgcHJpbnQocGFzdGUoeCwgIioiLHksIj0iLHJlc3VsdCkpCiAgeSA8LSB5KzF9CgoKIyAxfjEwIOq5jOyngOydmCDsp53siJjsl5DshJwgNOunjCDsoJzsmbjtlZwg7IiYIOy2nOugpSAKeDwtMTsgYTwtMQp3aGlsZShhPD0xMCkgewogIHg8LSBjKHgsIGEpCiAgeTwteFt4JSUyPT0wXQogIGlmKGE9PTEwKSB7cHJpbnQoeVt5IT00XSl9IAogIGE8LWErMX0gCgoKYGBgCgojIOuNsOydtO2EsCDsi5zqsIHtmZQKIyMgMS7qs6DsiJjspIAg6re4656Y7ZS97IqkIO2VqOyImCAKYGBge3Ig7IKw7KCQ64+EIHBsb3QoKX0Kcm0obGlzdD1scygpKQoKIyMg7IKw7KCQ64+EIDogcGxvdCh4LCB5LCB0eXBlPSJwIiwgbWFpbj0i7KCc66qpIiwgc3ViPSLrtoDsoJzrqqkiLCB4bGFiPSJ47LaVIOygnOuqqSIsIHlsYWI9InnstpUg7KCc66qpIikKIyDsmLXshZjrk6QgCiMgdHlwZSA9ICLslrTsqYzqtawiICA9PiDsoJDsnZgg7ZiV7IudCiMgOiBwIOygkCwgbCDshKAsIGIg7KCQJuyEoCwgYyDslZ7snZgg6rKD7JeQ7IScIOygkOydgCDsl4bripQg6rKDLCBv7KCQ7ZSM66GvIOyEoO2UjOuhryDspJHssqksIGgg6rCB7KCQ7JeQ7IScIHjstpXquYzsp4DsnZgg7IiY7KeB7ISgLCBz7Jm87Kq96rCS7J2EIOq4sOy0iOuhnCDqs4Tri6gg66qo7JaR7Jy866GcIOyXsOqysCwgUyDsmKTrpbjsqr0g6rCS7J2EIOq4sOy0iOuhnCDqs4Tri6jrqqjslpHsnLzroZwg7Jew6rKwLCBuIOy2leunjOq3uOumrOqzoCDtlIzroa8g7JeGIAoKIyBsdHkgPSAi7Ja07KmM6rWsIiA9PiDshKDsnZgg7ZiV7IudICh0eXBlPSJsIuyduCDqsr3smrAg7IKs7JqpKQojIGJsYW5rIO2IrOuqheyEoCwgc29saWQg7Iuk7ISgLCBkYXNoZWQg64yA7Iuc7ISgLCBkb3R0ZWQg64+E7Yq47ISgLCBkb3RkYXNoIOuPhO2KuOyZgCDrjIDsi5zshKAsIGxvbmdkYXNoIOq4tCDrjIDsi5zshKAsIHR3b2Rhc2ggMuqwnOydmCDrjIDsi5zshKAgCgojIHhsaW09YygsKSwgeWxpbT1jKCwpIDogeCx5IOyijO2RnOydmCDstZzshp/qsJIsIOy1nOuMk+qwkiDsp4DsoJUgCgojIGFubj1GIDog7LaV7J2YIOygnOuqqSDtkZzsi5ztlZjsp4Ag7JWK7J2MICh4bGFiPSIiLCB5bGFiPSIi7JmAIOqwmeydgCDtmqjqs7wpCgojIGF4ZXM9RiA6IOy2lSDsoJzqsbAgCgojIOygkOydmCDsooXrpZgo7IOJLCDquLDtmLgpCiMgY29sPSJibGFjayIsIGNvbD0xIOydtOufsOyLneycvOuhnCDsg4nshKTsoJUg6rCA64qlLCDssKjroYDroZwg6rKA7KCVIOu5qOqwlSDstIjroZ0g7YyM656RIOyXsO2MjOuekSDrs7Trnbwg64W4656RIO2ajOyDiQojIHBjaD0xICDsoJDsnZgg66qo7JaRIOyngOyglSgwfjI1KQojIGNleD0wICDsoJDsnZgg7YGs6riwIOyngOyglQojIGx3ZD0xICDshKDsnZgg6rW16riwIOyngOyglSjthYzrkZDrpqwpCiMgYmc9ImJsdWUiIOygkOydmCDrsLDqsr3sg4kg7KeA7KCVKHBjaD0yMX4yNSDqsJLsl5Drp4wg7ZW064u5KQoKY2FycyAgICMgUuydmCDrgrTsnqUg642w7J207YSw7J24IGNhcnMg7IK07Y6067O06riwCnBsb3QoY2FycyRzcGVlZCwgY2FycyRkaXN0KSAjIOq4sOuzuCDsgrDsoJDrj4QgCnBsb3QoY2FycyRzcGVlZCwgY2FycyRkaXN0LCBtYWluPSJHcmFwaCBvZiBzcGVlZCBhbmQgZGlzdGFuY2UiKSAjIOygnOuqqeu2gOyXrCAKcGxvdChjYXJzJHNwZWVkLCBjYXJzJGRpc3QsIG1haW49IkdyYXBoIG9mIHNwZWVkIGFuZCBkaXN0YW5jZSIsIHhsYWI9InNwZWVkIiwgeWxhYj0iZGlzdGFuY2UiKSAjIHgseSDstpUg652867KoIOyngOyglQoKIyB0eXBlIOyYteyFmCA6IOyCsOygkOuPhCDsooXrpZgKeCA8LSBjYXJzJHNwZWVkCnkgPC0gY2FycyRkaXN0CnBhcihtZnJvdz1jKDIsNSkpICMgMu2WiSA17Je0CnBsb3QoeCwgeSwgdHlwZT0icCIpCnBsb3QoeCwgeSwgdHlwZT0ibCIpCgoKIyBsdHkg7Ji17IWYIDog7ISgIOyiheulmAo/Qk9EICMg642w7J207YSwIOyCtO2OtOuztOq4sAp4IDwtIEJPRCRUaW1lCnkgPC0gQk9EJGRlbWFuZApwYXIobWZyb3c9YygyLDMpKQpwbG90KHgsIHksIHR5cGU9ImwiLCBsdHk9MSkgIyDsnbTroIfqsowg7Iir7J6Q66GcIOyNqOuPhCDrmJHqsJnsnYAg7Zqo6rO8LgpwbG90KHgsIHksIHR5cGU9ImwiLCBsdHk9MikKCgojIOygkOydmCDsooXrpZgsIOyDiQpwYXIobWZyb3c9YygyLDMpKQpwbG90KHgsIHksIGNvbD0yKSAKcGxvdCh4LCB5LCBjb2w9J2JsdWUnKSAjIO2FjOuRkOumrOyDiSAKcGxvdCh4LCB5LCBjb2w9MiwgcGNoPTApICMg66qo7JaRCnBsb3QoeCwgeSwgY29sPTIsIHBjaD0wLCBjZXg9MikgIyDtgazquLAKcGxvdCh4LCB5LCBjb2w9MiwgcGNoPTAsIGNleD0yLCBsd2Q9MikgICMg7YWM65GQ66asIOyEoCDqtbXquLAgCnBsb3QoeCwgeSwgY29sPTIsIHBjaD0yMSwgY2V4PTIsIGx3ZD0yLCBiZz0nYmx1ZScpICMg7LGE7Jqw6riwIOyDiS4gcGNo6rCAIDIxfjI17J24IOqyveyasOqwgCBiZ+qwgCDsoIHsmqkKCgojIyBwYWlycygpIDog7IKw7KCQ64+EIO2WieugrC4g67OA7IiYIOyMjSDqsITsnZgg7IKw7KCQ64+E65OkIOuqveuVhSDqt7jrpqzquLAgCnBhaXJzKGlyaXNbMTo0XSkKcGFpcnMoaXJpc1sxOjRdLCBsb3dlci5wYW5lbD1OVUxMKSAjIOuMgOqwgeyEoCDslYTrnpgg7Yyo64SQIO2RnOyLnO2VmOyngCDslYrquLAKcGFpcnMoaXJpc1sxOjRdLCB1cHBlci5wYW5lbD1OVUxMKSAjIOuMgOqwgeyEoCDsnIQg7Yyo64SQIO2RnOyLnO2VmOyngCDslYrquLAKIyBsYWJhbHMgOiDrs4DsiJjrk6TsnZgg7J2066aE7J2EIOyngOygle2VmOuKlCDsmLXshZgKYGBgCgoKYGBge3Ig66eJ64yA6re4656Y7ZSEIGJhcnBsb3QoKX0KIyMg66eJ64yA6re4656Y7ZSEIDogYmFycGxvdChoZWlnaHQ9642w7J207YSwLCB3aWR0aD3rp4nrjIDtj60sIHNwYWNlPeunieuMgOqwhOqyqSwgY29sPeyDiSwgbmFtZXMuYXJnPeunieuMgOudvOuyqCwgaG9yaXo9VOuptOunieuMgOulvCDsiJjtj4nsnLzroZwpIApmcnVpdHNfY291bnQgPC0gYyg0LDMsNSw4KQpuYW1lcyhmcnVpdHNfY291bnQpIDwtIGMoIkFwcGxlIiwiVG9tYXRvIiwiR3JhcGUiLCJNZWxvbiIpIDsgZnJ1aXRzX2NvdW50CmJhcnBsb3QoaGVpZ2h0PWZydWl0c19jb3VudCkKYmFycGxvdChoZWlnaHQ9ZnJ1aXRzX2NvdW50LCB3aWR0aD1jKDAuNSwxLDEuNSwyKSkKYmFycGxvdChoZWlnaHQ9ZnJ1aXRzX2NvdW50LCBzcGFjZT0yKQoKYmFycGxvdChoZWlnaHQ9ZnJ1aXRzX2NvdW50LCBjb2w9YygibGlnaHRibHVlIiwibWlzdHlyb3NlIiwibGlnaHRjeWFuIiwibGF2ZW5kZXIiKSkgIyDsg4nsp4DsoJUgCmJhcnBsb3QoaGVpZ2h0PWMoNCwzLDUsOCksIG5hbWVzLmFyZz1jKCJBcHBsZSIsIlRvbWF0byIsIkdyYXBlIiwiTWVsb24iKSkgIyDrp4nrjIDrk6Qg7J2066aEIOyngOyglSAKYmFycGxvdChoZWlnaHQ9ZnJ1aXRzX2NvdW50LCBob3Jpej1UUlVFKSAjIOunieuMgOulvCDsiJjtj4nsnLzroZwKYmFycGxvdChoZWlnaHQ9ZnJ1aXRzX2NvdW50LCBob3Jpej1GKSAjIOunieuMgOulvCDsiJjsp4HsnLzroZwgKOuUlO2PtO2KuCkKYGBgCgoKYGBge3Ig7Z6I7Iqk7Yag6re4656oIGhpc3QoKX0KIyMg7Z6I7Iqk7Yag6re4656oIDogaGlzdCh4PeuNsOydtO2EsCwgYnJlYWtzPeunieuMgOq1rOqwhG9y66eJ64yA6rCv7IiYb3LsnpDrj5nsnLzroZzsqrzqsJzquLAsIGZyZXE9VOuptOyEuOuhnOy2leu5iOuPhCBG66m067CA64+ELCBwcm9iYWJpbGl0eT1U66m07IS466Gc7LaV67CA64+EIEbrqbQg67mI64+EKQpjbGFzc19oZWlnaHQgPC0gYygxNzMsMTYzLDE3NCwxNjgsMTc1LDE3OCwxNjYsMTc4LDE3MywxODUpCmhpc3QoeD1jbGFzc19oZWlnaHQpCmhpc3QoeD1jbGFzc19oZWlnaHQsIGJyZWFrcz1jKDE2MCwxNzAsMTgwLDE5MCkpICMg6rWs6rCEIOygle2VmOq4sApoaXN0KHg9Y2xhc3NfaGVpZ2h0LCBwcm9iYWJpbGl0eT1UUlVFKQpoaXN0KHg9Y2xhc3NfaGVpZ2h0LCBwcm9iPVRSVUUpCgojI1BhZ2UgMjQKeCA8LSBybm9ybSgxMDAwKTt4ICAgIyDsoJXqt5zrtoTtj6zrpbwg65Sw66W064qUIOuCnOyImCAxMDAw6rCcIOy2lOy2nApoaXN0KHgpICMgZGVmYXVsdOuKlCDruYjrj4QoZnJlcXVlbmN5KQpoaXN0KHgsIHByb2I9VCkgICAjIOuwgOuPhOuhnCDrgpjtg4Drg4QKCiMgYnJlYWtzIOyekOuPmeycvOuhnCDqtazqsIQg7Kq86rCc6riwIAp4IDwtIHJub3JtKDEwMDAwKQpwYXIobWZyb3c9YygxLDMpKQpoaXN0KHgsIGJyZWFrcz0iU3R1cmdlcyIsIG1haW49IlN0dXJnZXMiKSAjIOuUlO2PtO2KuCwgCmhpc3QoeCwgYnJlYWtzPSJTY290dCIsIG1haW49IlNjb3R0IikKaGlzdCh4LCBicmVha3M9IkZyZWVkbWFuLURpYWNvbmlzIiwgbWFpbj0iRnJlZWRtYW4tRGlhY29uaXMiKQoKIyBuY2xhc3Mg66Gc64+EIOq1rOqwhCDsqrzqsKQg7IiYIOyeiOydjCEgCnggPC0gcm5vcm0oMTAwLDMsMik7IHggPC0gcm91bmQoeCwyKTt4CnBhcihtZnJvdz1jKDEsMykpCmhpc3QoeCwgbmNsYXNzPTEwLG1haW49Im5jbGFzcz0xMCIpIApoaXN0KHgsIG5jbGFzcz0yMCxtYWluPSJuY2xhc3M9MjAiKQpoaXN0KHgsIG5jbGFzcz0zMCxtYWluPSJuY2xhc3M9MzAiKQoKIyB57LaVIOyXhuyVoOq4sCAKaGlzdCh4LCBheGVzPVQsIG1haW49ImF4ZXM9VCIpCmhpc3QoeCwgYXhlcz1GLCBtYWluPSJheGVzPUYiKQoKCgojIyDsl7DsirXrrLjsoJwKIyMgMeuyiCA6IFLsl5Ag64K07J6l65CcIGlyaXMg642w7J207YSw7JeQ7IScIOyCsOygkOuPhCDtlonroKzsnZgg6re466CkIOuzgOyImOuTpOqwhOydmCDsg4HqtIDqtIDqs4Qg7YyM7JWF7ZWY6riwCmhlYWQoaXJpcykKc3RyKGlyaXMpICMgU3BlY2llcyDrs4DsiJjripQgRmFjdG9yLCDrgpjrqLjsp4Ag67OA7IiY65Ok7J2AIOyImOy5mO2YlSDrs4DsiJjsnoTsnYQg7ZmV7J24CnBhaXJzKGlyaXNbMTo0XSkgIyDsiJjsuZjtmJUg67OA7IiY65Ok66eMIOqwluqzoCDsgrDsoJDrj4Qg7ZaJ66CsIOq3uOumrOq4sAoKIyMgMuuyiCA6IFLsl5Ag64K07J6l65CcIGNhcnMg642w7J207YSw7JeQ7IScIHjstpXsnYQgZGlzdCwgeey2leydhCBzcGVlZOuhnCDtlZjripQg7IKw7KCQ64+EIOq3uOuemO2UhCDqt7jrpqzquLAKcGxvdChjYXJzJGRpc3QsIGNhcnMkc3BlZWQsIGNvbD0nYmx1ZScsIHhsYWI9J2Rpc3RhbmNlJywgeWxhYj0nc3BlZWQnLCBtYWluPSdwbG90IG9mIGRpc3RhbmNlIGFuZCBzcGVlZCcpCgojIyAz67KIIDog7Y+J6reg7J20IDPsnbgg7Y+s7JWE7IahIOu2hO2PrOyXkOyEnCAxMOqwnOydmCDrgpzsiJjrpbwg7LaU7Lac7ZWY7JesIOunieuMgOq3uOuemO2UhCDqt7jrpqzquLAKeDwtcnBvaXMoMTAsMyk7eApiYXJwbG90KHgpCgojIyA067KIIDog7Y+J6reg7J20IDTsnbTqs6Ag7ZGc7KWw7Y647LCo6rCAIDLsnbgg7KCV6rec67aE7Y+s7JeQ7IScIDEwMDAw6rCc7J2YIOuCnOyImOulvCDstpTstpztlZjsl6wgeey2leydtCDrsIDrj4TqsIAg65CY6rKMIO2VmOuKlCDtnojsiqTthqDqt7jrnqgg6re466as6riwCnk8LXJub3JtKDEwMDAwLCA0LCAyKTt5Cmhpc3QoeSwgZnJlcXVlbmN5PUYpCmhpc3QoeSwgZnJlcT1GKQpoaXN0KHksIHByb2JhYmlsaXR5PVQpCmhpc3QoeSwgcHJvYj1UKQoKYGBgCgpgYGB7ciDrsojsmbg6IOuCnOyImOy2lOy2nH0KcnVuaWYoNCkgIyDqt6DsnbzrtoTtj6woMH4xKQpybm9ybShuPTMsIG1lYW49MCwgc2Q9MSApICAjIOygleq3nOu2hO2PrApycG9pcyhuPTMsIGxhbWJkYSA9IDMpICMg7Y+s7JWE7Iah67aE7Y+sCnNhbXBsZSgxOjUsIDMsIHJlcGxhY2U9VCwgcHJvYj1jKDAuMSwgMC4xLCAwLjEsIDAuMSwgMC42KSkgIyDrgrTqsIAg6rCA7KeEIOuNsOydtO2EsOyXkOyEnCDstpTstpwgLiAKIyBzYW1wbGUo64K06rCAIOqwgOynhCDrjbDsnbTthLAsIOqwnOyImCwgcmVwbGFjZSA9IOuwmOuzteyXrOu2gCwgcHJvYiA9IO2ZleuloOyEpOyglSkKYGBgCgoKCgpgYGB7cn0KCmBgYAoKCg==