데이터베이스 분석하기

필요한 패키지

if("rJava" %in% installed.packages("rJava") == FALSE)install.packages("rJava")
library(rJava)
if("DBI" %in% installed.packages("DBI") == FALSE)install.packages("DBI")
library(DBI)
if("RJDBC" %in% installed.packages("RJDBC") == FALSE)install.packages("RJDBC")
library(RJDBC)
if("XML" %in% installed.packages("XML") == FALSE)install.packages("XML")
if("data.table" %in% installed.packages("data.table") == FALSE)install.packages("data.table")
library(data.table)
if("dplyr" %in% installed.packages("dplyr") == FALSE)install.packages("dplyr")
library(dplyr)

Oracle 데이터를 Rstudio 로 가져오는 방법

myJDBC <- JDBC("oracle.jdbc.driver.OracleDriver",
               "C:\\oraclexe\\app\\oracle\\product\\11.2.0\\server\\jdbc\\lib\\ojdbc6.jar")
conn <- dbConnect(myJDBC,
                  "jdbc:oracle:thin:@localhost:1521:xe",
                  "hr",
                  "oracle"
)
tab <- dbGetQuery(conn,"SELECT * FROM TAB")
tab

dplyr

filter, select, arrange, mutate,

summarise, group_by, left_join, bind_rows

tab <- data.frame(tab)
View(tab)
tname <- tab$TNAME
tname <- as.vector(tname)
tname

[1] “COUNTRIES” cnt “DEPARTMENTS” dep

[3] “DEPARTMENTS” emp “EMP_DETAILS_VIEW” empd

[5] “JOBS” job “JOB_HISTORY” jobh

[7] “LOCATIONS” loc “REGIONS” reg

cnt <- data.frame(dbGetQuery(conn,"SELECT * FROM COUNTRIES"))
head(cnt)
dep <- data.frame(dbGetQuery(conn,"SELECT * FROM DEPARTMENTS")) 
head(dep)
emp <- data.frame(dbGetQuery(conn,"SELECT * FROM DEPARTMENTS"))  
head(emp)
empd <- data.frame(dbGetQuery(conn,"SELECT * FROM EMP_DETAILS_VIEW"))  
head(empd)
job <- data.frame(dbGetQuery(conn,"SELECT * FROM JOBS"))  
head(job)
jobh <- data.frame(dbGetQuery(conn,"SELECT * FROM JOB_HISTORY")) 
head(jobh)
loc <- data.frame(dbGetQuery(conn,"SELECT * FROM LOCATIONS"))  
head(loc)
reg <- data.frame(dbGetQuery(conn,"SELECT * FROM REGIONS")) 
head(reg)

cnt의 컬럼명을 한글로 전환하시오

str(cnt)

COUNTRY_ID 국가 아이디,

COUNTRY_NAME 국가명

REGION_ID 지역아이디

cnt <- cnt %>% 
  dplyr::rename(국가아이디 = COUNTRY_ID ,
                     국가명 = COUNTRY_NAME,
                     지역아이디 = REGION_ID
  )
str(cnt)

dep 의 컬럼명 전환

str(dep)

DEPARTMENT_ID부서아이디

DEPARTMENT_NAME 부서며

MANAGER_ID 명매니저 아이디

LOCATION_ID 위치아이디

dep <- dep %>% 
  dplyr::rename(부서아이디 = DEPARTMENT_ID,
                     부서명 = DEPARTMENT_NAME,
                     매니저아이디 = MANAGER_ID,
                     위치아이디 = LOCATION_ID)
head(dep)

emp 의 컬럼명을 한글로 전환하시오.

그리고 First Name 과 Last Name 을

붙여서 이름 으로 된 컬럼을 추가하시오

단, 이름 간격은 띄울것. ex) James Dean

직원아이디 = EMPLOYEE_ID

이메일 = EMAIL

전화번호 = PHONE_NUMBER

채용일 = HIRE_DATE

업무아이디 = JOB_ID

연봉 = SALARY

커미션비율 = COMMISSION_PCT

매니저아이디 = MANAGER_ID

부서아이디 = DEPARTMENT_ID

str(emp)

emp <- emp %>% 
  dplyr::rename(직원아이디 = EMPLOYEE_ID,
                     이메일 = EMAIL,
                     전화번호 = PHONE_NUMBER,
                     채용일 = HIRE_DATE,
                     업무아이디 = JOB_ID,
                     연봉 = SALARY,
                     커미션비율 = COMMISSION_PCT,
                     매니저아이디 = MANAGER_ID,
                     부서아이디 = DEPARTMENT_ID
                     )
mutate(이름 = paste(FIRST_NAME,
                  LAST_NAME))
View(head(emp))

필요 없는 변수 삭제하기

emp <- subset(emp, select = c(FIRST_NAME, LAST_NAME)) 
emp <- subset(emp, select = -c(FIRST_NAME, LAST_NAME)) #서로 반대됨 
View(head(emp))

매달 지급하는 월급여(연봉/12)를 보여주는

월급이란는 컬럼 (변수)를 추가시키시오 0단위 이하는 절삭

emp_1 <- emp
  emp_1 
  mutate(월급 = 연봉%/%12) %>% 
  subset(emp, filter(월급<=0) )
View(head(emp_1))
#=====쌤 코딩 ==========
emp <- emp %>% 
    dplyr::mutate(월급 = 연봉%/%12)  #?
emp %>% head()  
View(emp)
  subset(emp, filter(월급<=0) )
View(head(emp))

연봉이 10000불 이상인 사원(emp)의 목록을 이름 지원아이디, 연봉만

구하되 연봉 내림차순으로 보여주시오.

str(emp)
  emp %>% left_join(dep, by="부서아이디") %>% 
  filter(연봉 >= 100000) %>% 
  select(이름 , 지원아이디,연봉,부서명 ) %>% 
  arrange(desc(연봉))

연봉 3000미만인 사원에게보너스로 급여의 1%를 지급 하겠다고

합니다. 보너스 컬럼을 추가 하고 대상자의 목록을 이름 , 직원아이디, 연봉, 보너스 을 #기재하고

아이디 오름차순으로 보여주시오

단 보너스는 이번달만 주는 것이므로 emp DF에 저장하지 말고 1회용 임시 DF를 따로 생성해서

저장하고 기한이 지나서는 폐기 하라.

보너스지급명세서  <-  emp %>% 
  filter(연봉 < 3000) %>% 
  dplyr:: mutate(보너스 = 연봉*0.01) %>%   
  select(이름 ,지원아이디,연봉, 보너스 ) %>% 
  arrange(직원아이디)
View(보너스지급명세서) 
rm(보너스지급명세서)#변수 제거 함수 

변수 추가하기

연봉이 10000이 넘는 직원의 부서명, 이름 ,연봉, 출력하시오 #출력하시오 <-사용안함

emp %>% left_join(dep,by ="부서아이디 " ) %>% #= raw 데이터 (값 )이다,그래서 "" 사용함 
  filter (연봉>=10000) %>% 
  select(부서명 ,이름 , 연봉)#연봉 변수임, 그래서 ""사용안함

부서별로 연봉 평균을 구하시오

emp %>% 
  left_join(dep,by ="부서아이디 " ) %>%
  View

dep %>% 
  left_join(emp, by="부서아이디 ") %>% 
  group_by(부서명,부서아이디 )#부서별로 이므로 group_by()사용함 
 dplyr::summarise(연봉평균=mean(연봉 ,na.rm=T)) %>% #na.rm=T->na이면 rm 삭제 함 
   arrange(desc(연봉평균 )) %>% 
   View

이회사의 부서의 수를 구하시오

dep %>% 
   dplyr::distinct(부서명 ) %>% #distinct는 중복을 제거하기 
   count

연봉이 12000넘는 직원의 부서명 , 이름 ,연봉, 직책을 기재하시오.

job <- job %>% 
   rename(업무아이디 = JOB_ID,
               직책 = JOB_TITLE,
               최소연봉 = MIN_SALARY,
               최대연봉 = MAX_SALARY
               )
 job %>% View

dep %>% 
  left_join(emp, by="부서아이디") %>% 
  left_join(job, by="업무아이디") %>% 
  filter(연봉>=12000) %>% 
  select(부서명, 이름, 연봉,직책 )

부서별로 가장 높은 연봉을 받는 부서아이디, 부서명 ,최대연봉을 구하시오

emp %>% 
  left_join(dep, by="부서아이디") %>% 
  left_join(job, by="업무아이디") %>% 
  group_by(부서아이디,부서명  ) %>% 
  summary(max(연봉 ))

부서아이디를 발급받지 않으면 신입입니다.신입의 이름과 연봉을 구하시오

View(emp)
emp %>% 
  filter(is.na(emp %>% select(부서아이디))) %>% 
  select(이름,연봉,부서아이디 )

#===혼자 한것 ====
emp <- ifelse (is.na(emp$부서아이디), 신입,emp$부서아이) %>% 
  filter(부서아이디=="신입 ") %>% 
  select(이름,연봉)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyMjIyO1pcDMxc26o8DMvbogutC8rsfPseINCiMjI8fKv+TH0SDG0MWwwfYNCmBgYHtyfQ0KaWYoInJKYXZhIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygickphdmEiKSA9PSBGQUxTRSlpbnN0YWxsLnBhY2thZ2VzKCJySmF2YSIpDQpsaWJyYXJ5KHJKYXZhKQ0KaWYoIkRCSSIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoIkRCSSIpID09IEZBTFNFKWluc3RhbGwucGFja2FnZXMoIkRCSSIpDQpsaWJyYXJ5KERCSSkNCmlmKCJSSkRCQyIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoIlJKREJDIikgPT0gRkFMU0UpaW5zdGFsbC5wYWNrYWdlcygiUkpEQkMiKQ0KbGlicmFyeShSSkRCQykNCmlmKCJYTUwiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCJYTUwiKSA9PSBGQUxTRSlpbnN0YWxsLnBhY2thZ2VzKCJYTUwiKQ0KaWYoImRhdGEudGFibGUiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCJkYXRhLnRhYmxlIikgPT0gRkFMU0UpaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQppZigiZHBseXIiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCJkcGx5ciIpID09IEZBTFNFKWluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KIyMjIyBPcmFjbGUgtaXAzMXNuKYgUnN0dWRpbyC3ziCwocGuv8C0wiC55rn9DQpgYGB7cn0NCm15SkRCQyA8LSBKREJDKCJvcmFjbGUuamRiYy5kcml2ZXIuT3JhY2xlRHJpdmVyIiwNCiAgICAgICAgICAgICAgICJDOlxcb3JhY2xleGVcXGFwcFxcb3JhY2xlXFxwcm9kdWN0XFwxMS4yLjBcXHNlcnZlclxcamRiY1xcbGliXFxvamRiYzYuamFyIikNCmNvbm4gPC0gZGJDb25uZWN0KG15SkRCQywNCiAgICAgICAgICAgICAgICAgICJqZGJjOm9yYWNsZTp0aGluOkBsb2NhbGhvc3Q6MTUyMTp4ZSIsDQogICAgICAgICAgICAgICAgICAiaHIiLA0KICAgICAgICAgICAgICAgICAgIm9yYWNsZSINCikNCnRhYiA8LSBkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gVEFCIikNCnRhYg0KYGBgDQojIGRwbHlyDQojIGZpbHRlciwgc2VsZWN0LCBhcnJhbmdlLCBtdXRhdGUsIA0KIyBzdW1tYXJpc2UsIGdyb3VwX2J5LCBsZWZ0X2pvaW4sIGJpbmRfcm93cw0KYGBge3J9DQp0YWIgPC0gZGF0YS5mcmFtZSh0YWIpDQpWaWV3KHRhYikNCnRuYW1lIDwtIHRhYiRUTkFNRQ0KdG5hbWUgPC0gYXMudmVjdG9yKHRuYW1lKQ0KdG5hbWUNCmBgYA0KIyBbMV0gIkNPVU5UUklFUyIgY250ICAgICAgICJERVBBUlRNRU5UUyIgZGVwICAgICANCiMgWzNdICJERVBBUlRNRU5UUyIgZW1wICAgICAgICAiRU1QX0RFVEFJTFNfVklFVyIgZW1wZA0KIyBbNV0gIkpPQlMiIGpvYiAgICAgICAgICAgICAiSk9CX0hJU1RPUlkiIGpvYmggICAgIA0KIyBbN10gIkxPQ0FUSU9OUyIgbG9jICAgICAgICJSRUdJT05TIiByZWcNCmBgYHtyfQ0KY250IDwtIGRhdGEuZnJhbWUoZGJHZXRRdWVyeShjb25uLCJTRUxFQ1QgKiBGUk9NIENPVU5UUklFUyIpKQ0KaGVhZChjbnQpDQpkZXAgPC0gZGF0YS5mcmFtZShkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gREVQQVJUTUVOVFMiKSkgDQpoZWFkKGRlcCkNCmVtcCA8LSBkYXRhLmZyYW1lKGRiR2V0UXVlcnkoY29ubiwiU0VMRUNUICogRlJPTSBERVBBUlRNRU5UUyIpKSAgDQpoZWFkKGVtcCkNCmVtcGQgPC0gZGF0YS5mcmFtZShkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gRU1QX0RFVEFJTFNfVklFVyIpKSAgDQpoZWFkKGVtcGQpDQpqb2IgPC0gZGF0YS5mcmFtZShkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gSk9CUyIpKSAgDQpoZWFkKGpvYikNCmpvYmggPC0gZGF0YS5mcmFtZShkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gSk9CX0hJU1RPUlkiKSkgDQpoZWFkKGpvYmgpDQpsb2MgPC0gZGF0YS5mcmFtZShkYkdldFF1ZXJ5KGNvbm4sIlNFTEVDVCAqIEZST00gTE9DQVRJT05TIikpICANCmhlYWQobG9jKQ0KcmVnIDwtIGRhdGEuZnJhbWUoZGJHZXRRdWVyeShjb25uLCJTRUxFQ1QgKiBGUk9NIFJFR0lPTlMiKSkgDQpoZWFkKHJlZykNCmBgYA0KI2NudMDHICDEw7ezuO3AuyDH0bHbt84gwPzIr8fPvcO/wA0KYGBge3J9DQpzdHIoY250KQ0KYGBgDQojQ09VTlRSWV9JRCCxubChIL7GwMy18CwNCiNDT1VOVFJZX05BTUUgsbmwobjtDQojUkVHSU9OX0lEIMH2v6q+xsDMtfANCg0KYGBge3J9DQpjbnQgPC0gY250ICU+JSANCiAgZHBseXI6OnJlbmFtZSixubChvsbAzLXwID0gQ09VTlRSWV9JRCAsDQogICAgICAgICAgICAgICAgICAgICCxubChuO0gPSBDT1VOVFJZX05BTUUsDQogICAgICAgICAgICAgICAgICAgICDB9r+qvsbAzLXwID0gUkVHSU9OX0lEDQogICkNCnN0cihjbnQpDQpgYGANCg0KDQojZGVwIMDHIMTDt7O47SDA/MivDQpgYGB7cn0NCnN0cihkZXApDQpgYGANCg0KIyBERVBBUlRNRU5UX0lEus68rb7GwMy18A0KI0RFUEFSVE1FTlRfTkFNRSC6zrytuOcNCiNNQU5BR0VSX0lEILjtuMW0z8D6IL7GwMy18A0KI0xPQ0FUSU9OX0lEIMCnxKG+xsDMtfAgDQpgYGB7cn0NCmRlcCA8LSBkZXAgJT4lIA0KICBkcGx5cjo6cmVuYW1lKLrOvK2+xsDMtfAgPSBERVBBUlRNRU5UX0lELA0KICAgICAgICAgICAgICAgICAgICAgus68rbjtID0gREVQQVJUTUVOVF9OQU1FLA0KICAgICAgICAgICAgICAgICAgICAguMW0z8D6vsbAzLXwID0gTUFOQUdFUl9JRCwNCiAgICAgICAgICAgICAgICAgICAgIMCnxKG+xsDMtfAgPSBMT0NBVElPTl9JRCkNCmhlYWQoZGVwKQ0KYGBgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyBlbXAgwMcgxMO3s7jtwLsgx9Gx27fOIMD8yK/Hz73Dv8AuDQojIyCx17iusO0gRmlyc3QgTmFtZSCw+iBMYXN0IE5hbWUgwLsNCiMjILrZv6m8rSDAzLinIMC4t84gtcggxMO3s8C7IMPfsKHHz73Dv8ANCiMjILTcLCDAzLinILCjsN3AuiC257/vsM0uIGV4KSBKYW1lcyBEZWFuDQojIyDB97/4vsbAzLXwID0gRU1QTE9ZRUVfSUQNCiMjIMDMuN7AzyA9IEVNQUlMDQojIyDA/MitufjIoyA9IFBIT05FX05VTUJFUg0KIyMgw6S/68DPID0gSElSRV9EQVRFDQojIyC+97mrvsbAzLXwID0gSk9CX0lEDQojIyC/rLrAID0gU0FMQVJZDQojIyDEv7nMvMe68cCyID0gQ09NTUlTU0lPTl9QQ1QNCiMjILjFtM/A+r7GwMy18CA9IE1BTkFHRVJfSUQNCiMjILrOvK2+xsDMtfAgPSBERVBBUlRNRU5UX0lEDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmBgYHtyfQ0Kc3RyKGVtcCkNCg0KZW1wIDwtIGVtcCAlPiUgDQogIGRwbHlyOjpyZW5hbWUowfe/+L7GwMy18CA9IEVNUExPWUVFX0lELA0KICAgICAgICAgICAgICAgICAgICAgwMy43sDPID0gRU1BSUwsDQogICAgICAgICAgICAgICAgICAgICDA/MitufjIoyA9IFBIT05FX05VTUJFUiwNCiAgICAgICAgICAgICAgICAgICAgIMOkv+vAzyA9IEhJUkVfREFURSwNCiAgICAgICAgICAgICAgICAgICAgIL73uau+xsDMtfAgPSBKT0JfSUQsDQogICAgICAgICAgICAgICAgICAgICC/rLrAID0gU0FMQVJZLA0KICAgICAgICAgICAgICAgICAgICAgxL+5zLzHuvHAsiA9IENPTU1JU1NJT05fUENULA0KICAgICAgICAgICAgICAgICAgICAguMW0z8D6vsbAzLXwID0gTUFOQUdFUl9JRCwNCiAgICAgICAgICAgICAgICAgICAgILrOvK2+xsDMtfAgPSBERVBBUlRNRU5UX0lEDQogICAgICAgICAgICAgICAgICAgICApDQptdXRhdGUowMy4pyA9IHBhc3RlKEZJUlNUX05BTUUsDQogICAgICAgICAgICAgICAgICBMQVNUX05BTUUpKQ0KVmlldyhoZWFkKGVtcCkpDQoNCmBgYA0KI8fKv+Qgvvi0wiC6r7z2ILvowabHz7HiDQpgYGB7cn0NCmVtcCA8LSBzdWJzZXQoZW1wLCBzZWxlY3QgPSBjKEZJUlNUX05BTUUsIExBU1RfTkFNRSkpIA0KZW1wIDwtIHN1YnNldChlbXAsIHNlbGVjdCA9IC1jKEZJUlNUX05BTUUsIExBU1RfTkFNRSkpICO8rbfOILndtOu1yiANClZpZXcoaGVhZChlbXApKQ0KYGBgDQojuMW03iDB9rHex8+0wiC/+bHev6kov6y6wC8xMim4piC6uL+pwda0wiANCiO/+bHewMy29bTCIMTDt7MgKLqvvPYpuKYgw9+wob3DxbC9w7/AIDC03MCnIMDMx8+0wiDA/bvoDQpgYGB7cn0NCmVtcF8xIDwtIGVtcA0KICBlbXBfMSANCiAgbXV0YXRlKL/5sd4gPSC/rLrAJS8lMTIpICU+JSANCiAgc3Vic2V0KGVtcCwgZmlsdGVyKL/5sd48PTApICkNClZpZXcoaGVhZChlbXBfMSkpDQojPT09PT293CDE2rX5ID09PT09PT09PT0NCmVtcCA8LSBlbXAgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUov/mx3iA9IL+susAlLyUxMikgICM/DQplbXAgJT4lIGhlYWQoKSAgDQpWaWV3KGVtcCkNCiAgc3Vic2V0KGVtcCwgZmlsdGVyKL/5sd48PTApICkNClZpZXcoaGVhZChlbXApKQ0KYGBgDQojv6y6wMDMIDEwMDAwutIgwMy788DOILvnv/goZW1wKcDHILjxt8/AuyDAzLinIMH2v/i+xsDMtfAsIL+susC4uCANCiOxuMfPtccgv6y6wCCzu7iywve8+MC4t84guri/qcHWvcO/wC4NCmBgYHtyfQ0Kc3RyKGVtcCkNCiAgZW1wICU+JSBsZWZ0X2pvaW4oZGVwLCBieT0ius68rb7GwMy18CIpICU+JSANCiAgZmlsdGVyKL+susAgPj0gMTAwMDAwKSAlPiUgDQogIHNlbGVjdCjAzLinICwgwfa/+L7GwMy18Cy/rLrALLrOvK247SApICU+JSANCiAgYXJyYW5nZShkZXNjKL+susApKQ0KYGBgDQoNCiO/rLrAIDMwMDC5zLi4wM4gu+e/+L+hsNS6uLPKvbq3ziCx3r+pwMcgMSW4piDB9rHeIMfPsNq02bDtIA0KI8fVtM+02S4gurizyr26IMTDt7PAuyDD37ChIMfPsO0gtOu788DawMcguPG3z8C7IMDMuKcgLCDB97/4vsbAzLXwLCC/rLrALCC6uLPKvbogwLsgI7HiwOfHz7DtIA0KI77GwMy18CC/wLinwve8+MC4t84guri/qcHWvcO/wA0KI7TcILq4s8q9urTCIMDMufi03ri4IMHWtMIgsM3AzLnHt84gZW1wIERGv6EgwPrA5cfPwfYguLuw7SAxyLi/6yDA073DIERGuKYgtfu3ziC7/by6x9i8rQ0KI8D6wOXHz7DtILHix9HAzCDB9rOqvK20wiDG87HiIMfPtvMuDQpgYGB7cn0NCrq4s8q9usH2sd647by8vK0gIDwtICBlbXAgJT4lIA0KICBmaWx0ZXIov6y6wCA8IDMwMDApICU+JSANCiAgZHBseXI6OiBtdXRhdGUourizyr26ID0gv6y6wCowLjAxKSAlPiUgICANCiAgc2VsZWN0KMDMuKcgLMH2v/i+xsDMtfAsv6y6wCwgurizyr26ICkgJT4lIA0KICBhcnJhbmdlKMH3v/i+xsDMtfApDQpWaWV3KLq4s8q9usH2sd647by8vK0pIA0Kcm0ourizyr26wfax3rjtvLy8rSkjuq+89iDBprDFIMfUvPYgDQoNCmBgYA0KIyC6r7z2IMPfsKHHz7HiIA0KI7+susDAzCAxMDAwMMDMILPRtMIgwfe/+MDHILrOvK247SwgwMy4pyAsv6y6wCwgw+K3wsfPvcO/wCAjw+K3wsfPvcO/wCA8Lbvnv+u+yMfUDQpgYGB7cn0NCmVtcCAlPiUgbGVmdF9qb2luKGRlcCxieSA9IrrOvK2+xsDMtfAgIiApICU+JSAjPSByYXcgtaXAzMXNICiwqiApwMy02Syx17ehvK0gIiIgu+e/68fUIA0KICBmaWx0ZXIgKL+susA+PTEwMDAwKSAlPiUgDQogIHNlbGVjdCi6zrytuO0gLMDMuKcgLCC/rLrAKSO/rLrAILqvvPbA0ywgsde3obytICIiu+e/677Ix9QNCmBgYA0KI7rOvK26sLfOIL+susAgxvKx1cC7ILG4x8+9w7/ADQpgYGB7cn0NCmVtcCAlPiUgDQogIGxlZnRfam9pbihkZXAsYnkgPSK6zrytvsbAzLXwICIgKSAlPiUNCiAgVmlldw0KDQpkZXAgJT4lIA0KICBsZWZ0X2pvaW4oZW1wLCBieT0ius68rb7GwMy18CAiKSAlPiUgDQogIGdyb3VwX2J5KLrOvK247Sy6zrytvsbAzLXwICkjus68rbqwt84gwMy5x7fOIGdyb3VwX2J5KCm757/rx9QgDQogZHBseXI6OnN1bW1hcmlzZSi/rLrAxvKx1T1tZWFuKL+susAgLG5hLnJtPVQpKSAlPiUgI25hLnJtPVQtPm5hwMy46SBybSC76MGmIMfUIA0KICAgYXJyYW5nZShkZXNjKL+susDG8rHVICkpICU+JSANCiAgIFZpZXcNCmBgYA0KI8DMyLi758DHILrOvK3AxyC89rimILG4x8+9w7/AIA0KYGBge3J9DQpkZXAgJT4lIA0KICAgZHBseXI6OmRpc3RpbmN0KLrOvK247SApICU+JSAjZGlzdGluY3S0wiDB37q5wLsgwaawxcfPseIgDQogICBjb3VudA0KYGBgDQojv6y6wMDMIDEyMDAws9G0wiDB97/4wMcgus68rbjtICwgwMy4pyAsv6y6wCwgwffDpcC7ILHiwOfHz73Dv8AuDQpgYGB7cn0NCmpvYiA8LSBqb2IgJT4lIA0KICAgcmVuYW1lKL73uau+xsDMtfAgPSBKT0JfSUQsDQogICAgICAgICAgICAgICDB98OlID0gSk9CX1RJVExFLA0KICAgICAgICAgICAgICAgw9a80r+susAgPSBNSU5fU0FMQVJZLA0KICAgICAgICAgICAgICAgw9a067+susAgPSBNQVhfU0FMQVJZDQogICAgICAgICAgICAgICApDQogam9iICU+JSBWaWV3DQoNCmRlcCAlPiUgDQogIGxlZnRfam9pbihlbXAsIGJ5PSK6zrytvsbAzLXwIikgJT4lIA0KICBsZWZ0X2pvaW4oam9iLCBieT0ivve5q77GwMy18CIpICU+JSANCiAgZmlsdGVyKL+susA+PTEyMDAwKSAlPiUgDQogIHNlbGVjdCi6zrytuO0sIMDMuKcsIL+susAswffDpSApDQpgYGANCiMjus68rbqwt84gsKHA5SCz9MC6IL+susDAuyC53rTCILrOvK2+xsDMtfAsILrOvK247SAsw9a067+susDAuyCxuMfPvcO/wA0KYGBge3J9DQplbXAgJT4lIA0KICBsZWZ0X2pvaW4oZGVwLCBieT0ius68rb7GwMy18CIpICU+JSANCiAgbGVmdF9qb2luKGpvYiwgYnk9Ir73uau+xsDMtfAiKSAlPiUgDQogIGdyb3VwX2J5KLrOvK2+xsDMtfAsus68rbjtICApICU+JSANCiAgc3VtbWFyeShtYXgov6y6wCApKQ0KYGBgDQojus68rb7GwMy18LimILnfsd653sH2IL7KwLi46SC9xcDUwNS0z7TZLr3FwNTAxyDAzLinsPogv6y6wMC7ILG4x8+9w7/ADQpgYGB7cn0NClZpZXcoZW1wKQ0KZW1wICU+JSANCiAgZmlsdGVyKGlzLm5hKGVtcCAlPiUgc2VsZWN0KLrOvK2+xsDMtfApKSkgJT4lIA0KICBzZWxlY3QowMy4pyy/rLrALLrOvK2+xsDMtfAgKQ0KDQojPT09yKXA2iDH0bDNID09PT0NCmVtcCA8LSBpZmVsc2UgKGlzLm5hKGVtcCS6zrytvsbAzLXwKSwgvcXA1CxlbXAkus68rb7GwMwpICU+JSANCiAgZmlsdGVyKLrOvK2+xsDMtfA9PSK9xcDUICIpICU+JSANCiAgc2VsZWN0KMDMuKcsv6y6wCkNCmBgYA0KDQoNCg==