필요한 패키지들을 깐다.

list("ggplot2", "plyr", "dplyr", "car") %>% 
  map(library)
Error in .f(.x[[i]], ...) : 'package' must be of length 1

필요한 라이브러리들을 불러들인다.

requiredPackages <- c("MASS", "purrr", "ggplot2", "plyr","dplyr","car")
ipak <- function(pkg){
        new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
        if (length(new.pkg))
                install.packages(new.pkg, dependencies = TRUE)
        sapply(pkg, require, character.only = TRUE)
}
ipak(requiredPackages)
Loading required package: MASS

Attaching package: 愼㸱愼㹥MASS愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:dplyr愼㸱愼㹦:

    select

Loading required package: plyr
-----------------------------------------------------------------------------
You have loaded plyr after dplyr - this is likely to cause problems.
If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
library(plyr); library(dplyr)
-----------------------------------------------------------------------------

Attaching package: 愼㸱愼㹥plyr愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:purrr愼㸱愼㹦:

    compact

The following objects are masked from 愼㸱愼㹥package:dplyr愼㸱愼㹦:

    arrange, count, desc, failwith, id, mutate, rename, summarise,
    summarize

Loading required package: car
Loading required package: carData

Attaching package: 愼㸱愼㹥car愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:purrr愼㸱愼㹦:

    some

The following object is masked from 愼㸱愼㹥package:dplyr愼㸱愼㹦:

    recode
   MASS   purrr ggplot2    plyr   dplyr     car 
   TRUE    TRUE    TRUE    TRUE    TRUE    TRUE 

각 데이터의 행과 열을 살펴보자

library(purrr)
list(products,search1, search2, custom) %>% 
  map(dim)
[[1]]
[1] 5024906       8

[[2]]
[1] 2884943       4

[[3]]
[1] 8051172       3

[[4]]
[1] 671679      3

Custom테이블 살펴보기

dim(custom) # 67만명의 데이터가 있다.  671,679
[1] 671679      3
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
  input string 1 is invalid in this locale

고객들의 분포를 살펴보자

table(custom$CLNT_AGE, custom$CLNT_GENDER)
    
          F      M
  10   3007    800
  20  82269  13028
  30 271712  41888
  40 180398  35819
  50  28881   7743
  60   3784   1472
  70    542    304
  80     23      9
tmp6 <- table(custom$CLNT_AGE, custom$CLNT_GENDER)
tmp6 <- as.data.frame(tmp6)
colnames(tmp6)[1:2] <- c("age","gender")
library(ggplot2)
ggplot(tmp6, aes(age, Freq, group=gender, col=gender)) +
  geom_line()

  • 여성 고객의 비율이 많다.
  • 30대 여성의 비율이 가장 높다.

producst테이블 살펴보기 ——————————————————–

Sys.setlocale("LC_ALL","Korean")
[1] "LC_COLLATE=Korean_Korea.949;LC_CTYPE=Korean_Korea.949;LC_MONETARY=Korean_Korea.949;LC_NUMERIC=C;LC_TIME=Korean_Korea.949"
products <- as.data.frame(products)
dim(products) # 500만건
[1] 5024906       8
colnames(products)
[1] "CLNT_ID"   "SESS_ID"   "HITS_SEQ"  "PD_C"      "PD_ADD_NM" "PD_BRA_NM"
[7] "PD_BUY_AM" "PD_BUY_CT"
head(products)
products$SESS_ID <- sprintf("%08d", as.integer(products$SESS_ID)) #

# tmp <- products[order(products$HITS_SEQ), ] # hits sequence에 따라 정렬
# tmp[order(tmp$SESS_ID), ]

동일한 세션 아이디가 몇개 있는지 살펴보자

고유한 세션 아이디가 전체의 48프로. 52프로는 반복된 세션이라는 말…

length(unique(products$SESS_ID)) / nrow(products) 
[1] 0.4827724

master테이블 살펴보기 ———————————————————-

head(master)
Error in head(master) : object 'master' not found
search1 <- as.data.frame(search1)
search2 <- as.data.frame(search2)

# search 테이블 살펴보기 ---------------------------------------------------------

하루당 검색건수를 알아보자.

counts <- search2 %>% 
  group_by(SESS_DT) %>% 
  dplyr::summarise(searching_occured = n(), number_of_na = sum(is.na()) )
Error in summarise_impl(.data, dots) : 
  Evaluation error: 0 arguments passed to 'is.na' which requires 1.

YYYYMMDD 형태를 날짜 변수로 바꾸는 방법.

counts$SESS_DT <- as.Date(as.character(counts[["SESS_DT"]]), "%Y%m%d")
counts <- as.data.frame(counts)

그래프로 그려보자.

datebreaks <- seq(min(counts$SESS_DT), max(counts$SESS_DT), by="1 month")
library(scales)
ggplot(counts, aes(SESS_DT, searching_occured)) +
  geom_line()+
  scale_x_date(breaks = datebreaks, labels = date_format("%Y%m"))+
  theme(axis.text.x = element_text(angle=30, hjust=1))

일주일에 한번씩 검색량의 등락이 보인다. 총 26번의 등락이 있고, 이는 6개월(26주)를 의미한다.

검색어의 길이에 따라 검색량에 차이가 있는지 살펴보자.

검색어 길이가 긴 것은 A로 정의하고, 짧은 그룹을 B라고 정의하자. 검색어 길이가 길면 길수록 특수성이 올라가기 때문에, hit수가 적을 것으로 추정된다. 실제로 그런가, 검정해보자.

tmp[tmp$cnt <= 5, ]$group <- "B"
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
  input string 1 is invalid in this locale

t-검정을 실시해보자.

t.test(chr ~ group, data = tmp, var.equal=TRUE)

    Two Sample t-test

data:  chr by group
t = -113.58, df = 8050600, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.2115216 -0.2043454
sample estimates:
mean in group A mean in group B 
       5.867973        6.075906 

두 집단 사이에 차이가 있는 것으로 보인다.

Session 테이블을 살펴보자

head(session)
session <- as.data.frame(session)
sort(table(session$ZON_NM), decreasing = T)

            Seoul       Gyeonggi-do             Busan  Gyeongsangnam-do 
          1019843            469954            295940            128716 
            Daegu           Incheon  Gyeongsangbuk-do      Jeollabuk-do 
           116707            100728             91183             78437 
Chungcheongnam-do           Gwangju           Daejeon      Jeollanam-do 
            78072             70773             60442             49299 
            Ulsan Chungcheongbuk-do        Gangwon-do           Jeju-do 
            46584             44455             42701             19073 
plot(sort(table(session$ZON_NM), decreasing = T))

한 세션당 클릭수는 얼마나 되나 살펴보자. 카이제곱 분포 모양과 흡사하다.

얼마나 오래 머무는지 살펴보자.

plot(sort(table(session$TOT_SESS_HR_V), decreasing = T))
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
  input string 1 is invalid in this locale
abline(v=1800, col="red") # 세션이 만료되는 30분 지점. 

plot(sort(table(session$TOT_SESS_HR_V), decreasing = T), xlim=c(0,1440))

한 세션에 39329초(32329/60 = 655시간) 머무는 데이터가 있다. 아웃라이어로 간주된다. 아니면 매크로 봇이거나….

날짜별 접속한 세션들의 수(고객수)를 집계해보자.

counting_table <- plyr::count(session, 'SESS_DT')
datebreaks <- seq(min(counting_table$SESS_DT), max(counting_table$SESS_DT), by="1 month")
library(scales)
ggplot(counting_table, aes(SESS_DT,freq)) +
  geom_line()+
  scale_x_date(breaks = datebreaks, labels = date_format("%Y%m"))+
  theme(axis.text.x = element_text(angle=30, hjust=1))

기기별 사용자수를 살펴보자.

모바일 기기 사용자수가 제일 많다.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IHBhcGVyDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0K7ZWE7JqU7ZWcIO2MqO2CpOyngOuTpOydhCDquZDri6QuIA0KDQpgYGB7cn0NCndhbnRzIDwtIGMoICJkcGx5ciIsImdncGxvdDIiLCJwdXJyciIgKQ0KaGFzICAgPC0gd2FudHMgJWluJSByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkNCmlmKGFueSghaGFzKSkgaW5zdGFsbC5wYWNrYWdlcyh3YW50c1shaGFzXSkNCg0KDQoNCmBgYA0KDQrtlYTsmpTtlZwg65287J2067iM65+s66as65Ok7J2EIOu2iOufrOuTpOyduOuLpC4gDQpgYGB7cn0NCg0KcmVxdWlyZWRQYWNrYWdlcyA8LSBjKCJNQVNTIiwgInB1cnJyIiwgImdncGxvdDIiLCAicGx5ciIsImRwbHlyIiwiY2FyIikNCg0KDQppcGFrIDwtIGZ1bmN0aW9uKHBrZyl7DQogICAgICAgIG5ldy5wa2cgPC0gcGtnWyEocGtnICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCAiUGFja2FnZSJdKV0NCiAgICAgICAgaWYgKGxlbmd0aChuZXcucGtnKSkNCiAgICAgICAgICAgICAgICBpbnN0YWxsLnBhY2thZ2VzKG5ldy5wa2csIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICAgIHNhcHBseShwa2csIHJlcXVpcmUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KaXBhayhyZXF1aXJlZFBhY2thZ2VzKQ0KYGBgDQoNCg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQ0KDQoNCiMgMjAxOOyekOujjCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KbGlicmFyeShyZWFkcikNCnByb2R1Y3RzIDwtIHJlYWRfY3N2KCJFOi9Ecm9wYm94LzIwMTcvMDYuam9iX3JlY3J1aXRtZW50LzEzLnByb2dyYW1taW5nLzA1Lkxwb2ludC9kYXRhLzIwMTgvMDEuUHJ1ZHVjdC5jc3YiLA0KICBsb2NhbGUgPSBsb2NhbGUoKQ0KKQ0Kc2VhcmNoMSA8LSByZWFkX2NzdigiRTovRHJvcGJveC8yMDE3LzA2LmpvYl9yZWNydWl0bWVudC8xMy5wcm9ncmFtbWluZy8wNS5McG9pbnQvZGF0YS8yMDE4LzAyLlNlYXJjaDEuY3N2IiwNCiAgbG9jYWxlID0gbG9jYWxlKCkNCikNCnNlYXJjaDIgPC0gcmVhZF9jc3YoIkU6L0Ryb3Bib3gvMjAxNy8wNi5qb2JfcmVjcnVpdG1lbnQvMTMucHJvZ3JhbW1pbmcvMDUuTHBvaW50L2RhdGEvMjAxOC8wMy5TZWFyY2gyLmNzdiIsDQogIGxvY2FsZSA9IGxvY2FsZSgpDQopDQpjdXN0b20gPC0gcmVhZF9jc3YoIkU6L0Ryb3Bib3gvMjAxNy8wNi5qb2JfcmVjcnVpdG1lbnQvMTMucHJvZ3JhbW1pbmcvMDUuTHBvaW50L2RhdGEvMjAxOC8wNC5DdXN0b20uY3N2IiwNCiAgbG9jYWxlID0gbG9jYWxlKCkNCikNCnNlc3Npb24gPC0gcmVhZF9jc3YoIkU6L0Ryb3Bib3gvMjAxNy8wNi5qb2JfcmVjcnVpdG1lbnQvMTMucHJvZ3JhbW1pbmcvMDUuTHBvaW50L2RhdGEvMjAxOC8wNS5TZXNzaW9uLmNzdiIsDQogIGxvY2FsZSA9IGxvY2FsZSgpDQopDQptYXN0ZXIgPC0gcmVhZF9jc3YoIkU6L0Ryb3Bib3gvMjAxNy8wNi5qb2JfcmVjcnVpdG1lbnQvMTMucHJvZ3JhbW1pbmcvMDUuTHBvaW50L2RhdGEvMjAxOC8wNi5NYXN0ZXIuY3N2IiwNCiAgbG9jYWxlID0gbG9jYWxlKCkNCikNCg0KYGBgDQoNCg0K6rCBIOuNsOydtO2EsOydmCDtlonqs7wg7Je07J2EIOyCtO2OtOuztOyekA0KYGBge3J9DQoNCmxpYnJhcnkocHVycnIpDQpsaXN0KHByb2R1Y3RzLHNlYXJjaDEsIHNlYXJjaDIsIGN1c3RvbSkgJT4lIA0KICBtYXAoZGltKQ0KYGBgDQoNCiMgQ3VzdG9t7YWM7J2067iUIOyCtO2OtOuztOq4sA0KDQpgYGB7cn0NCg0KIyBjdXN0b23thYzsnbTruJQg7IK07Y6067O06riwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmN1c3RvbSA8LSBhcy5kYXRhLmZyYW1lKGN1c3RvbSkNCg0KZGltKGN1c3RvbSkgIyA2N+unjOuqheydmCDrjbDsnbTthLDqsIAg7J6I64ukLiAgNjcxLDY3OQ0KDQpgYGANCg0KIyMg6rOg6rCd65Ok7J2YIOu2hO2PrOulvCDsgrTtjrTrs7TsnpAgDQoNCmBgYHtyfQ0KdGFibGUoY3VzdG9tJENMTlRfQUdFLCBjdXN0b20kQ0xOVF9HRU5ERVIpDQp0bXA2IDwtIHRhYmxlKGN1c3RvbSRDTE5UX0FHRSwgY3VzdG9tJENMTlRfR0VOREVSKQ0KdG1wNiA8LSBhcy5kYXRhLmZyYW1lKHRtcDYpDQpjb2xuYW1lcyh0bXA2KVsxOjJdIDwtIGMoImFnZSIsImdlbmRlciIpDQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QodG1wNiwgYWVzKGFnZSwgRnJlcSwgZ3JvdXA9Z2VuZGVyLCBjb2w9Z2VuZGVyKSkgKw0KICBnZW9tX2xpbmUoKQ0KDQpgYGANCi0g7Jes7ISxIOqzoOqwneydmCDruYTsnKjsnbQg66eO64ukLg0KLSAzMOuMgCDsl6zshLHsnZgg67mE7Jyo7J20IOqwgOyepSDrhpLri6QuIA0KDQoNCiMgcHJvZHVjc3TthYzsnbTruJQg7IK07Y6067O06riwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpgYGB7cn0NClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsIktvcmVhbiIpDQoNCnByb2R1Y3RzIDwtIGFzLmRhdGEuZnJhbWUocHJvZHVjdHMpDQpkaW0ocHJvZHVjdHMpICMgNTAw66eM6rG0DQpjb2xuYW1lcyhwcm9kdWN0cykNCmhlYWQocHJvZHVjdHMpDQoNCmBgYA0KDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQoNCnByb2R1Y3RzJFNFU1NfSUQgPC0gc3ByaW50ZigiJTA4ZCIsIGFzLmludGVnZXIocHJvZHVjdHMkU0VTU19JRCkpICMNCg0KIyB0bXAgPC0gcHJvZHVjdHNbb3JkZXIocHJvZHVjdHMkSElUU19TRVEpLCBdICMgaGl0cyBzZXF1ZW5jZeyXkCDrlLDrnbwg7KCV66CsDQojIHRtcFtvcmRlcih0bXAkU0VTU19JRCksIF0NCg0KDQpgYGANCg0KDQojIyDrj5nsnbztlZwg7IS47IWYIOyVhOydtOuUlOqwgCDrqofqsJwg7J6I64qU7KeAIOyCtO2OtOuztOyekA0K6rOg7Jyg7ZWcIOyEuOyFmCDslYTsnbTrlJTqsIAg7KCE7LK07J2YIDQ47ZSE66GcLiA1Mu2UhOuhnOuKlCDrsJjrs7XrkJwg7IS47IWY7J20652864qUIOunkC4uLg0KYGBge3J9DQpsZW5ndGgodW5pcXVlKHByb2R1Y3RzJFNFU1NfSUQpKSAvIG5yb3cocHJvZHVjdHMpIA0KYGBgDQoNCg0KIyBtYXN0ZXLthYzsnbTruJQg7IK07Y6067O06riwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KYGBge3J9DQoNCmhlYWQobWFzdGVyKQ0KIyBzdHIobWFzdGVyKQ0KDQpgYGANCg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KDQpzZWFyY2gxIDwtIGFzLmRhdGEuZnJhbWUoc2VhcmNoMSkNCnNlYXJjaDIgPC0gYXMuZGF0YS5mcmFtZShzZWFyY2gyKQ0KDQojIHNlYXJjaCDthYzsnbTruJQg7IK07Y6067O06riwIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmBgYA0KDQoNCg0K7ZWY66Oo64u5IOqygOyDieqxtOyImOulvCDslYzslYTrs7TsnpAuIA0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQpjb3VudHMgPC0gc2VhcmNoMiAlPiUgDQogIGdyb3VwX2J5KFNFU1NfRFQpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZShzZWFyY2hpbmdfb2NjdXJlZCA9IG4oKSApDQoNCg0KYGBgDQoNCg0KIyMjIyBZWVlZTU1ERCDtmJXtg5zrpbwg64Kg7KecIOuzgOyImOuhnCDrsJTqvrjripQg67Cp67KVLiANCg0KYGBge3J9DQpjb3VudHMkU0VTU19EVCA8LSBhcy5EYXRlKGFzLmNoYXJhY3Rlcihjb3VudHNbWyJTRVNTX0RUIl1dKSwgIiVZJW0lZCIpDQpjb3VudHMgPC0gYXMuZGF0YS5mcmFtZShjb3VudHMpDQpgYGANCg0KDQoNCuq3uOuemO2UhOuhnCDqt7jroKTrs7TsnpAuIA0KYGBge3J9DQoNCg0KZGF0ZWJyZWFrcyA8LSBzZXEobWluKGNvdW50cyRTRVNTX0RUKSwgbWF4KGNvdW50cyRTRVNTX0RUKSwgYnk9IjEgbW9udGgiKQ0KDQpsaWJyYXJ5KHNjYWxlcykNCmdncGxvdChjb3VudHMsIGFlcyhTRVNTX0RULCBzZWFyY2hpbmdfb2NjdXJlZCkpICsNCiAgZ2VvbV9saW5lKCkrDQogIHNjYWxlX3hfZGF0ZShicmVha3MgPSBkYXRlYnJlYWtzLCBsYWJlbHMgPSBkYXRlX2Zvcm1hdCgiJVklbSIpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9MzAsIGhqdXN0PTEpKQ0KDQpgYGANCuydvOyjvOydvOyXkCDtlZzrsojslKkg6rKA7IOJ65+J7J2YIOuTseudveydtCDrs7Tsnbjri6QuIA0K7LSdIDI267KI7J2YIOuTseudveydtCDsnojqs6AsIOydtOuKlCA26rCc7JuUKDI27KO8KeulvCDsnZjrr7jtlZzri6QuIA0KDQoNCiMjIyDqsoDsg4nslrTsnZgg6ri47J207JeQIOuUsOudvCDqsoDsg4nrn4nsl5Ag7LCo7J206rCAIOyeiOuKlOyngCDsgrTtjrTrs7TsnpAuIA0KDQrqsoDsg4nslrQg6ri47J206rCAIOq4tCDqsoPsnYAgQeuhnCDsoJXsnZjtlZjqs6AsIOynp+ydgCDqt7jro7nsnYQgQuudvOqzoCDsoJXsnZjtlZjsnpAuIA0K6rKA7IOJ7Ja0IOq4uOydtOqwgCDquLjrqbQg6ri47IiY66GdIO2KueyImOyEseydtCDsmKzrnbzqsIDquLAg65WM66y47JeQLCBoaXTsiJjqsIAg7KCB7J2EIOqyg+ycvOuhnCDstpTsoJXrkJzri6QuIOyLpOygnOuhnCDqt7jrn7DqsIAsIOqygOygle2VtOuztOyekC4gDQoNCmBgYHtyfQ0KdG1wIDwtIGRhdGEuZnJhbWUoDQogIGNudCA9IHNlYXJjaDIkU0VBUkNIX0NOVCwNCiAgY2hyID0gYXBwbHkoc2VhcmNoMiwgMiwgbmNoYXIpWywyXQ0KKQ0KDQojIOqygOyDieufieyXkCDrlLDrpbgg6rKA7IOJ7Ja0IOq4uOydtCDssKjsnbTqsIAg7J6I64qU7KeAIOyCtO2OtOuztOyekC4NCnRtcCA8LSB0bXBbY29tcGxldGUuY2FzZXModG1wKSxdDQp0bXAgPC0gYXMuZGF0YS5mcmFtZSh0bXApDQp0bXAkZ3JvdXAgPC0gTkENCnRtcFt0bXAkY250ID4gNSwgXSRncm91cCA8LSAiQSINCnRtcFt0bXAkY250IDw9IDUsIF0kZ3JvdXAgPC0gIkIiDQoNCmBgYA0KDQoNCnQt6rKA7KCV7J2EIOyLpOyLnO2VtOuztOyekC4gDQoNCmBgYHtyICB9DQp0LnRlc3QoY2hyIH4gZ3JvdXAsIGRhdGEgPSB0bXAsIHZhci5lcXVhbD1UUlVFKQ0KDQoNCmBgYA0K65GQIOynkeuLqCDsgqzsnbTsl5Ag7LCo7J206rCAIOyeiOuKlCDqsoPsnLzroZwg67O07J2464ukLiANCg0KDQpgYGB7cn0NCmdncGxvdCh0bXAsIGFlcyhjaHIsIGNvbD1ncm91cCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFkanVzdD01KSsNCiAgeGxpbSgwLDIwKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChwcm9kdWN0cykNCmhlYWQoc2VhcmNoMSkNCmhlYWQoc2VhcmNoMikNCg0KYGBgDQoNCg0KDQojIFNlc3Npb24g7YWM7J2067iU7J2EIOyCtO2OtOuztOyekA0KYGBge3J9DQpoZWFkKHNlc3Npb24pDQoNCnNlc3Npb24kU0VTU19EVCA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihzZXNzaW9uW1siU0VTU19EVCJdXSksICIlWSVtJWQiKQ0Kc2Vzc2lvbiA8LSBhcy5kYXRhLmZyYW1lKHNlc3Npb24pDQoNCnNvcnQodGFibGUoc2Vzc2lvbiRaT05fTk0pLCBkZWNyZWFzaW5nID0gVCkNCnBsb3Qoc29ydCh0YWJsZShzZXNzaW9uJFpPTl9OTSksIGRlY3JlYXNpbmcgPSBUKSkNCg0KYGBgDQoNCiMjIO2VnCDshLjshZjri7kg7YG066at7IiY64qUIOyWvOuniOuCmCDrkJjrgpgg7IK07Y6067O07J6QLiDsubTsnbTsoJzqs7Eg67aE7Y+sIOuqqOyWkeqzvCDtnaHsgqztlZjri6QuIA0KYGBge3J9DQpwbG90KHNvcnQodGFibGUoc2Vzc2lvbiRUT1RfUEFHX1ZJRVdfQ1QpLCBkZWNyZWFzaW5nID0gVCkpDQoNCmBgYA0KDQrslrzrp4jrgpgg7Jik656YIOuouOustOuKlOyngCDsgrTtjrTrs7TsnpAuIA0KYGBge3J9DQpwbG90KHNvcnQodGFibGUoc2Vzc2lvbiRUT1RfU0VTU19IUl9WKSwgZGVjcmVhc2luZyA9IFQpKQ0KYWJsaW5lKHY9MTgwMCwgY29sPSJyZWQiKSAjIOyEuOyFmOydtCDrp4zro4zrkJjripQgMzDrtoQg7KeA7KCQLiANCg0KcGxvdChzb3J0KHRhYmxlKHNlc3Npb24kVE9UX1NFU1NfSFJfViksIGRlY3JlYXNpbmcgPSBUKSwgeGxpbT1jKDAsMTQ0MCkpDQoNCmBgYA0K7ZWcIOyEuOyFmOyXkCAzOTMyOey0iCgzMjMyOS82MCA9IDY1NeyLnOqwhCkg66i466y064qUIOuNsOydtO2EsOqwgCDsnojri6QuIA0K7JWE7JuD65287J207Ja066GcIOqwhOyjvOuQnOuLpC4g7JWE64uI66m0IOunpO2BrOuhnCDrtIfsnbTqsbDrgpguLi4uDQoNCg0KIyMg64Kg7Kec67OEIOygkeyGje2VnCDshLjshZjrk6TsnZgg7IiYKOqzoOqwneyImCnrpbwg7KeR6rOE7ZW067O07J6QLiANCmBgYHtyfQ0KDQpjb3VudGluZ190YWJsZSA8LSBwbHlyOjpjb3VudChzZXNzaW9uLCAnU0VTU19EVCcpDQoNCmRhdGVicmVha3MgPC0gc2VxKG1pbihjb3VudGluZ190YWJsZSRTRVNTX0RUKSwgbWF4KGNvdW50aW5nX3RhYmxlJFNFU1NfRFQpLCBieT0iMSBtb250aCIpDQoNCmxpYnJhcnkoc2NhbGVzKQ0KZ2dwbG90KGNvdW50aW5nX3RhYmxlLCBhZXMoU0VTU19EVCxmcmVxKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgc2NhbGVfeF9kYXRlKGJyZWFrcyA9IGRhdGVicmVha3MsIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlWSVtIikpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0zMCwgaGp1c3Q9MSkpDQoNCg0KYGBgDQoNCiMjIOq4sOq4sOuzhCDsgqzsmqnsnpDsiJjrpbwg7IK07Y6067O07J6QLiANCg0K66qo67CU7J28IOq4sOq4sCDsgqzsmqnsnpDsiJjqsIAg7KCc7J28IOunjuuLpC4gDQoNCmBgYHtyfQ0KYmFycGxvdCh0YWJsZShzZXNzaW9uJERWQ19DVEdfTk0pKQ0KDQoNCg0KYGBgDQoNCg0KDQoNCg0K