Introduction

Cohort Analysis 는 고객의 retention/chrun(이탈)에 대한 분석이다. 고객의 트렌드에 대한 이해를 높히며, 소비자 타켓을 구축하거나, 의사 결정에 도움을 주는 분석 기법이다.

UCI 의 Online Retail Data 를 활용하여 분석을 실시한다.

Preprocessing: Features

## tibble [541,909 x 8] (S3: tbl_df/tbl/data.frame)
##  $ InvoiceNo  : chr [1:541909] "536365" "536365" "536365" "536365" ...
##  $ StockCode  : chr [1:541909] "85123A" "71053" "84406B" "84029G" ...
##  $ Description: chr [1:541909] "WHITE HANGING HEART T-LIGHT HOLDER" "WHITE METAL LANTERN" "CREAM CUPID HEARTS COAT HANGER" "KNITTED UNION FLAG HOT WATER BOTTLE" ...
##  $ Quantity   : num [1:541909] 6 6 8 6 6 2 6 6 6 32 ...
##  $ InvoiceDate: POSIXct[1:541909], format: "2010-12-01 08:26:00" "2010-12-01 08:26:00" ...
##  $ UnitPrice  : num [1:541909] 2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
##  $ CustomerID : num [1:541909] 17850 17850 17850 17850 17850 ...
##  $ Country    : chr [1:541909] "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...

Create The Cohorts

  1. 각 고객별 InvoiceDate 를 고객들이 JOIN DATE 로 지정

  2. Cohort 2011 데이터에 JOIN DATE 와 구매 빈도를 병합 (Inner Join)

  3. 고객들의 JOIN DATE의 Month로 각 고객들을 라벨링 한다.

What`s cohort?

cohort 는 특정 시간에 비슷한 성향을 보이는 그룹을 말하는 것으로, 고객들이 실제 고객으로 되는 기준을 날짜로 정해준다. 각 마케팅 채널별로 세분화가 가능할 수 있다. 예를 들어, 온라인데이터에서 제공되는 날짜변수를 통해 월별/주별/분기별/년별 등으로 고객들을 세분화 시키는 분석법을 말한다.

Cohort the days between Invoice and Join Date

가입 날짜 (Join Date) 와 구매 날짜 (Invoice Date) 의 차이를 구해서, Active customer에 대한 cohort 설정

#----------------------------------------------------------------------
#   Invoice - Join = Days difftime 함수 사용 
#--------------------------------------------------------------------


as.numeric(difftime(cohorts2011$InvoiceDate, 
                    cohorts2011$Join_Date,
                    units = c("days"))) -> cohorts2011$days  #Unit 지정: 일 별 계산


#----------------------------------------------------------------------
#  days/30 = month & Invoice Date 일자 삭제 
#--------------------------------------------------------------------


cohorts2011 %>%  
  mutate(month = round(days/30)) %>% 
  mutate(InvoiceDate = format(InvoiceDate, "%Y-%m")) %>% 
  mutate(Join_Date = format(Join_Date, "%Y-%m")) -> df_co




#----------------------------------------------------------------------
#  Cohort 생성하기  
#--------------------------------------------------------------------

c ("Jan Cohort", 
   "Feb Cohort",
   "Mar Cohort",
   "Apr Cohort", 
   "May Cohort", 
   "Jun Cohort",
   "Jul Cohort",
   "Aug Cohort",
   "Sep Cohort",
   "Oct Cohort",
   "Nov Cohort",
   "Dec Cohort") -> groups



for (i in 1:12) {
  
  df_co[df_co$Cohort == i, "Cohort"] <- groups[i]
  
}

#----------------------------------------------------------------------
#   Cohort factor level 고정값 지정  
#--------------------------------------------------------------------
df_co$Cohort <- factor(df_co$Cohort,ordered = T,levels =c("Jan Cohort",
                                                           "Feb Cohort",
                                                            "Mar Cohort",
                                                            "Apr Cohort",
                                                            "May Cohort",
                                                            "Jun Cohort",
                                                            "Jul Cohort",
                                                            "Aug Cohort",
                                                            "Sep Cohort",
                                                             "Oct Cohort",
                                                             "Nov Cohort",
                                                             "Dec Cohort"))
#----------------------------------------------------------------------
#   Cohort factor 변환  
#--------------------------------------------------------------------

#str(cohorts2011$Cohort)

which(duplicated(df_co[-5:-6])) -> dup


df_1 <- df_co[-dup,]


#----------------------------------------------------------------------
# 프레임 변환하기 
#--------------------------------------------------------------------


dcast(df_1, Cohort ~ month, 
      value.var = "CustomerID",
      fun.aggregate = length) ->cohorts.wide



cw.retention <- cohorts.wide
cw.churn <-cohorts.wide




breaks <- quantile(cohorts.wide[,3:13], prob= seq(.05, .95, .05), na.rm = T) 
colors <- sapply(round(seq(155,80, length.out = length(breaks)+1),0),
                 function(x){rgb(x,x,155, maxColorValue = 155)})




datatable(cohorts.wide,
              class = 'cell-border stripe',
             rownames = FALSE,
             options = list(
               ordering=F,
               dom = 't',
               pageLength = 12) ) %>% 
              formatStyle("0",
                         backgroundColor = 'lightgrey',
                         fontWeight = 'bold') %>%
             formatStyle(names(cohorts.wide[c(-1,-2)]),fontWeight = 'bold',color = 'white', 
                         backgroundColor = styleInterval(breaks,colors))
## # A tibble: 401,604 x 11
## # Groups:   CustomerID [4,372]
##    InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID
##    <chr>     <chr>     <chr>          <dbl> <date>          <dbl>      <dbl>
##  1 536365    85123A    WHITE HANG~        6 2010-12-01       2.55      17850
##  2 536365    71053     WHITE META~        6 2010-12-01       3.39      17850
##  3 536365    84406B    CREAM CUPI~        8 2010-12-01       2.75      17850
##  4 536365    84029G    KNITTED UN~        6 2010-12-01       3.39      17850
##  5 536365    84029E    RED WOOLLY~        6 2010-12-01       3.39      17850
##  6 536365    22752     SET 7 BABU~        2 2010-12-01       7.65      17850
##  7 536365    21730     GLASS STAR~        6 2010-12-01       4.25      17850
##  8 536366    22633     HAND WARME~        6 2010-12-01       1.85      17850
##  9 536366    22632     HAND WARME~        6 2010-12-01       1.85      17850
## 10 536367    84879     ASSORTED C~       32 2010-12-01       1.69      13047
## # ... with 401,594 more rows, and 4 more variables: Country <chr>, year <dbl>,
## #   Month <date>, unit <chr>

Type of Cohorts

Time Cohorts : 제품과 서비스를 사용하기 시작한 시간 기준 (Month/ Quarter 기준)

Behavior Cohorts : Product 타입 기준 -> Design custome-made Service or product 전략 수립

Size Cohort : 제품과 서비스를 구매한 고객의 사이즈 기준: 특별 기간 동안 쓴 소비액

##   InvoiceNo          StockCode         Description           Quantity        
##  Length:401604      Length:401604      Length:401604      Min.   :-80995.00  
##  Class :character   Class :character   Class :character   1st Qu.:     2.00  
##  Mode  :character   Mode  :character   Mode  :character   Median :     5.00  
##                                                           Mean   :    12.18  
##                                                           3rd Qu.:    12.00  
##                                                           Max.   : 80995.00  
##   InvoiceDate                    UnitPrice          CustomerID   
##  Min.   :2010-12-01 08:26:00   Min.   :    0.00   Min.   :12346  
##  1st Qu.:2011-04-06 15:02:00   1st Qu.:    1.25   1st Qu.:13939  
##  Median :2011-07-29 15:40:00   Median :    1.95   Median :15145  
##  Mean   :2011-07-10 12:08:23   Mean   :    3.47   Mean   :15281  
##  3rd Qu.:2011-10-20 11:58:30   3rd Qu.:    3.75   3rd Qu.:16784  
##  Max.   :2011-12-09 12:50:00   Max.   :38970.00   Max.   :18287  
##    Country         
##  Length:401604     
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

Cohort Analysis Step

  1. Invoice Period : year/month 추출

  2. Cohort Group : year/month 기반 고객 첫 구매일 추출

  3. Cohort period/Cohort Index : 구매일 이후의 month 의 수

library(lubridate)
#------------------------------------------------------------------------
#   ID 별 첫 구매 추출 = retail_cohort
#------------------------------------------------------------------------

retail %>%  
  group_by(CustomerID) %>% 
  summarise(Month = floor_date(min(InvoiceDate), unit = "month")) -> retail_cohort

#------------------------------------------------------------------------
#   InvoiceDate 에서 Month 변수 생성 = retail
#------------------------------------------------------------------------

retail %>% 
  mutate(InvoiceMonth = floor_date(InvoiceDate, unit = "month")) -> retail


#------------------------------------------------------------------------
#   retail_cohort + retail 병합= 각 ID 별 최초 구매 일자를 더해준다. 
#------------------------------------------------------------------------

merge( retail, retail_cohort,
      by.x= "CustomerID",
      by.y = "CustomerID") -> retail_merge



retail_merge %>% 
  mutate(InvoiceMonth_num = month(InvoiceDate),  #구입 한 날의 월/년도
         InvoiceYear_num = year(InvoiceDate),
         CohorMonth_num = month(Month),     #구입 최초 일의 월/년 
         CohortYear_num = year(Month),
         Index = (InvoiceYear_num-CohortYear_num)*12 +(InvoiceMonth_num - CohorMonth_num)+1) -> retail_merge


#------------------------------------------------------------------------
#   최초 구매일, Index 기준 customer의 수 누적 표 
#------------------------------------------------------------------------
retail_merge %>% 
  group_by(Month, Index) %>% #최초구매일, Index
  summarise(Total_Customer = n_distinct(CustomerID)) %>% 
  pivot_wider(names_from = Index, values_from = Total_Customer) %>% 
  rename(DEC = 2, 
         JAN = 3, 
         FEB = 4, 
         MAR = 5, 
         APR = 6,
         MAY = 7, 
         JUN = 8, 
         JUL = 9, 
         AUG = 10, 
         SEP = 11, 
         OCT = 12, 
         NOV = 13,
         DEC_A = 14) -> cohort_counts


#------------------------------------------------------------------------
#   최초 기준 비율 계산 표 
#------------------------------------------------------------------------

cohort_counts %>%  
  mutate(TOTAL = round(DEC/DEC,3)*100,
         JAN_R = round(JAN/DEC,3) *100, 
         FEB_R = round(FEB/DEC,3) *100,
         MAR_R = round(MAR/DEC,3) *100, 
         APR_R = round(APR/DEC,3) *100, 
         MAY_R = round(MAR/DEC,3) *100, 
         JUN_R = round(JUN/DEC,3) *100, 

         JUL_R = round(JUL/DEC,3) *100,
         AUG_R = round(AUG/DEC,3) *100,  
         SEP_R = round(SEP/DEC,3) *100,
         OCT_R = round(OCT/DEC,3) *100,  
         NOV_R = round(NOV/DEC,3) *100,  
         DEC_R = round(DEC_A/DEC,3) *100) %>%  
  select(-c(2:14)) -> retention

retention
## # A tibble: 13 x 14
## # Groups:   Month [13]
##    Month               TOTAL JAN_R FEB_R MAR_R APR_R MAY_R JUN_R JUL_R AUG_R
##    <dttm>              <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 2010-12-01 00:00:00   100  36.6  32.3  38.4  36.3  38.4  36.3  34.9  35.4
##  2 2011-01-01 00:00:00   100  22.1  26.6  23    32.1  23    24.7  24.2  30  
##  3 2011-02-01 00:00:00   100  18.7  18.7  28.4  27.1  28.4  25.3  27.9  24.7
##  4 2011-03-01 00:00:00   100  15    25.2  19.9  22.3  19.9  26.8  23    27.9
##  5 2011-04-01 00:00:00   100  21.3  20.3  21    19.7  21    21.7  26     7.3
##  6 2011-05-01 00:00:00   100  19    17.3  17.3  20.8  17.3  26.4   9.5  NA  
##  7 2011-06-01 00:00:00   100  17.4  15.7  26.4  23.1  26.4   9.5  NA    NA  
##  8 2011-07-01 00:00:00   100  18.1  20.7  22.3  27.1  22.3  NA    NA    NA  
##  9 2011-08-01 00:00:00   100  20.7  24.9  24.3  12.4  24.3  NA    NA    NA  
## 10 2011-09-01 00:00:00   100  23.4  30.1  11.4  NA    11.4  NA    NA    NA  
## 11 2011-10-01 00:00:00   100  24    11.5  NA    NA    NA    NA    NA    NA  
## 12 2011-11-01 00:00:00   100  11.1  NA    NA    NA    NA    NA    NA    NA  
## 13 2011-12-01 00:00:00   100  NA    NA    NA    NA    NA    NA    NA    NA  
## # ... with 4 more variables: SEP_R <dbl>, OCT_R <dbl>, NOV_R <dbl>, DEC_R <dbl>

RFM with Modelling

R : 최근

F : 자주

M : 금액

## [1] "2011-12-09 12:50:00 UTC"
## [1] "2011-12-10 12:50:00 UTC"
## [1] "2010-12-01 08:26:00 UTC"
## # A tibble: 6 x 4
##   CustomerID Recency Frequency Monetary
##        <dbl>   <dbl>     <int>    <dbl>
## 1      12346     326         1   77184.
## 2      12347       3       182    4310 
## 3      12348      76        31    1797.
## 4      12349      19        73    1758.
## 5      12350     311        17     334.
## 6      12352      37        85    2506.

KMEANS

##      Recency   Frequency    Monetary
## 1 -0.8804868 10.84400785 13.79293005
## 2 -0.5117712  0.04986022 -0.00282326
## 3  1.5463756 -0.28003576 -0.15740003

K-Meoid

Cluster 1 : Recency 가 180 이상으로, 6개월 동안 구매 내역이 없는 그룹이다. 반면 빈도는 25, 평균 구매액은 GBP594 Cluster 2 : Recnecy 가 98 으로 3개월 동안 구매 내역이 없는 그룹이다. 빈도수는 41으로, 평균 861 을 구매했다. Cluster 3 : Recency 가 22 으로 마지막 구매일로 부터, 30일 이내 구매ㅎ를 한 그룹으로 142 빈도, 평균 3250 GBP 사용

LS0tDQp0aXRsZTogIkNvaG9ydCBBbmFseXNpcyINCmF1dGhvcjogIkRPRVVOIg0KZGF0ZTogJzIwMjEgMyA5ICcNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICMgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuDQogICAgIyBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZmxhdGx5Ig0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRSkNCg0KI2luc3RhbGwucGFja2FnZXMoInVzZWZ1bCIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KbGlicmFyeSh1c2VmdWwpDQpsaWJyYXJ5KGNsdXN0ZXIpICNrLW1lZG9pZCDtlajsiJggDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24gDQoNCkNvaG9ydCBBbmFseXNpcyDripQg6rOg6rCd7J2YIHJldGVudGlvbi9jaHJ1bijsnbTtg4gp7JeQIOuMgO2VnCDrtoTshJ3snbTri6QuIOqzoOqwneydmCDtirjroIzrk5zsl5Ag64yA7ZWcIOydtO2VtOulvCDrhpLtnojrqbAsIOyGjOu5hOyekCDtg4DsvJPsnYQg6rWs7LaV7ZWY6rGw64KYLCDsnZjsgqwg6rKw7KCV7JeQIOuPhOybgOydhCDso7zripQg67aE7ISdIOq4sOuyleydtOuLpC4gDQoNClVDSSDsnZggT25saW5lIFJldGFpbCBEYXRhIOulvCDtmZzsmqntlZjsl6wg67aE7ISd7J2EIOyLpOyLnO2VnOuLpC4gDQoNCiMgUHJlcHJvY2Vzc2luZzogRmVhdHVyZXMgDQoNCg0KYGBge3J9DQpzZXR3ZCgnQzovVXNlcnMvQWRtaW5pc3RyYXRvci9EZXNrdG9wL1IgQW5hbHlzaXMvRmFzdCBDYW1wdXMnKQ0KDQpPbmxpbmVfUmV0YWlsIDwtIHJlYWRfZXhjZWwoIk9ubGluZSBSZXRhaWwueGxzeCIpDQpWaWV3KE9ubGluZV9SZXRhaWwpDQoNCnN0cihPbmxpbmVfUmV0YWlsKQ0KYGBgDQoNCg0KIyMgUmVtb3ZpbmcgRHVwbGljYXRlIA0KDQog7KSR67O165CY64qUIOqwkuuTpOydhCDsoJzqsbDtlZzri6QgLSB1bmlxdWUgLyBkdXBsaWNhdGUg7ZWo7IiYIOyCrOyaqSANCg0KYGBge3J9DQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAg7LSdIDU0MTkwOSDqsJzsnZgg642w7J207YSwIOykkSwg7KSR67O17J20IOyXhuuKlCDrjbDsnbTthLDripQgNTM2NjQxIOqwnOydtOuLpC4gDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCm5yb3codW5pcXVlKE9ubGluZV9SZXRhaWwpKSAjNTM2NjQxDQpucm93KE9ubGluZV9SZXRhaWwpICM1NDE5MDkNCiANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAg7KSR67O1IOuNsOydtO2EsOydmCDsiJg6IDUyNjjqsJwgIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpucm93KE9ubGluZV9SZXRhaWwpLW5yb3codW5pcXVlKE9ubGluZV9SZXRhaWwpKQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgRHVwbGljYXRlZCDtlajsiJggOiAg7KSR67O16rCS7J2EIOygnOqxsO2VmOqzoCDsnKDsnbztlZwg6rCS66eMIOyEoOuzhO2VmOq4sCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyDspJHrs7XrkJwg642w7J207YSw66eMIOy2lOy2nCANCmR1cGVzIDwtIHdoaWNoKGR1cGxpY2F0ZWQoT25saW5lX1JldGFpbCkpDQoNCg0Kb25saW5lLnJldGFpbDIgPC0gT25saW5lX1JldGFpbFstZHVwZXMsXQ0KDQpgYGANCg0KIyMgTkEgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIEN1c3RvbWVyIElEIOqysOy4oey5mCAxMzUwMzfqsJwg7ZmV7J24IA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY29sU3Vtcyhpcy5uYShvbmxpbmUucmV0YWlsMikpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICDqsrDsuKHsuZjrpbwg7KCc6rGw7ZWcIOuNsOydtO2EsCBTZXQg7Zmc7JqpIGNvbXBsZXRlLmNhc2VzIO2VqOyImCDsgqzsmqkgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0Kb25saW5lLnJldGFpbDJbY29tcGxldGUuY2FzZXMob25saW5lLnJldGFpbDIpLF0gLT4gb25saW5lLnJldGFpbDMNCg0KDQpgYGANCg0KIyMgREFURVMgDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICBhcy5EYXRlIO2VqOyImOulvCDsgqzsmqntlZjsl6wg7KCV66asIC0gSW52b2ljZSBEYXRlIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpvbmxpbmUucmV0YWlsMyRJbnZvaWNlRGF0ZSA8LSBhcy5EYXRlKG9ubGluZS5yZXRhaWwzJEludm9pY2VEYXRlLCBmb3JtYXQ9IiVtLyVkLyVZIikNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIFllYXIg7ZWo7IiY66W8IOyCrOyaqe2VmOyXrCDrhYTrj4Qg7LaU7LacIC0gSW52b2ljZSBEYXRlIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpvbmxpbmUucmV0YWlsMyR5ZWFyIDwtIGFzLm51bWVyaWMoZm9ybWF0KG9ubGluZS5yZXRhaWwzJEludm9pY2VEYXRlLCAnJVknKSkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIDIwMTEg64WE64+EIOuNsOydtO2EsOunjCDstpTstpwgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmNvaG9ydDIwMTEgPC0gb25saW5lLnJldGFpbDNbb25saW5lLnJldGFpbDMkeWVhciA9PSAyMDExLF0NCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgQ3VzdG9tZXIgSUQsIEludm9pY2UgRGF0YSwgWWVhciDshKDrs4TsnYQg7Ya17ZW0IGNvaG9ydCDqt7jro7kg7KeA7KCVIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmNvaG9ydDIwMTEgJT4lIA0KICBzZWxlY3QoQ3VzdG9tZXJJRCwgSW52b2ljZURhdGUsIHllYXIpIC0+IGNvaG9ydDIwMTENCg0KDQpgYGANCg0KIyBDcmVhdGUgVGhlIENvaG9ydHMgDQoNCjEuIOqwgSDqs6DqsJ3rs4QgSW52b2ljZURhdGUg66W8IOqzoOqwneuTpOydtCBKT0lOIERBVEUg66GcIOyngOyglSANCg0KMi4gQ29ob3J0IDIwMTEg642w7J207YSw7JeQIEpPSU4gREFURSDsmYAg6rWs66ekIOu5iOuPhOulvCDrs5HtlakgKElubmVyIEpvaW4pDQoNCjMuIOqzoOqwneuTpOydmCBKT0lOIERBVEXsnZggTW9udGjroZwg6rCBIOqzoOqwneuTpOydhCDrnbzrsqjrp4Eg7ZWc64ukLiANCg0KIyMgV2hhdGBzIGNvaG9ydD8gDQoNCmNvaG9ydCDripQg7Yq57KCVIOyLnOqwhOyXkCDruYTsirftlZwg7ISx7Zal7J2EIOuztOydtOuKlCDqt7jro7nsnYQg66eQ7ZWY64qUIOqyg+ycvOuhnCwg6rOg6rCd65Ok7J20IOyLpOygnCDqs6DqsJ3snLzroZwg65CY64qUIOq4sOykgOydhCANCuuCoOynnOuhnCDsoJXtlbTspIDri6QuIOqwgSDrp4jsvIDtjIUg7LGE64SQ67OE66GcIOyEuOu2hO2ZlOqwgCDqsIDriqXtlaAg7IiYIOyeiOuLpC4g7JiI66W8IOuTpOyWtCwg7Jio65287J24642w7J207YSw7JeQ7IScIOygnOqzteuQmOuKlCDrgqDsp5zrs4DsiJjrpbwg7Ya17ZW0IOyblOuzhC/so7zrs4Qv67aE6riw67OEL+uFhOuzhCDrk7HsnLzroZwg6rOg6rCd65Ok7J2EIOyEuOu2hO2ZlCDsi5ztgqTripQg67aE7ISd67KV7J2EIOunkO2VnOuLpC4gDQoNCmBgYHtyfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIENvaG9ydDIwMTEg642w7J207YSw7JeQ7IScIOqwgSBJRCDrs4Qg6rWs66ekIO2an+yImCDrjbDsnbTthLAg7ZSE66CI7J6EIOyDneyEsSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmNvaG9ydDIwMTEgJT4lIA0KICBncm91cF9ieShDdXN0b21lcklEKSAlPiUgDQogIGNvdW50KCkgLT4gb3JkZXIuZnJlcXVlbmN5DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgQ29ob3J0MjAxMSDrjbDsnbTthLDsl5DshJwg6rCBIElEIOuzhCDstZzstIgg6rWs66ek7J28ID0gSk9JTiBEQVRFL01JTiDtlajsiJgg7KCB7JqpICANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmNvaG9ydDIwMTEgJT4lIA0KICBncm91cF9ieShDdXN0b21lcklEKSAlPiUgDQogIHN1bW1hcmlzZShJbnZvaWNlRGF0ZSA9IG1pbihJbnZvaWNlRGF0ZSkpIC0+IEpvaW4uZGF0ZQ0KDQojUmVuYW1pbmcgY29sdW1ucyANCg0KY29sbmFtZXMoSm9pbi5kYXRlKVsyXSA8LSAiSm9pbl9EYXRlIg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgQ29ob3J0MjAxMeqzvCBKb2luX2RhdGUg67OR7ZWpIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KbWVyZ2UoY29ob3J0MjAxMSwgSm9pbi5kYXRlLCANCiAgICAgIGJ5LnggPSAiQ3VzdG9tZXJJRCIsDQogICAgICBieS55ID0gIkN1c3RvbWVySUQiLA0KICAgICAgYWxsLnggPSBUUlVFKSAtPiBjb2hvcnRzMjAxMQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIE1vbnRoIOulvCDstpTstpztlZjsl6wsIENvaG9ydCDrs4DsiJgg7IOd7ISxIEV4KSBKT0lOX0RBVEXqsIAgMeyblOydtOuptCDrnbzrsqjrp4EgMSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmFzLm51bWVyaWMoZm9ybWF0KGNvaG9ydHMyMDExJEpvaW5fRGF0ZSwgIiVtIikpIC0+IGNvaG9ydHMyMDExJENvaG9ydA0KDQoNCkRUOjpkYXRhdGFibGUoaGVhZChjb2hvcnRzMjAxMSkpDQoNCmBgYA0KDQoNCiMgQ29ob3J0IHRoZSBkYXlzIGJldHdlZW4gSW52b2ljZSBhbmQgSm9pbiBEYXRlICANCg0K6rCA7J6FIOuCoOynnCAoSm9pbiBEYXRlKSDsmYAg6rWs66ekIOuCoOynnCAoSW52b2ljZSBEYXRlKSDsnZgg7LCo7J2066W8IOq1rO2VtOyEnCwgQWN0aXZlIGN1c3RvbWVy7JeQIOuMgO2VnCBjb2hvcnQg7ISk7KCVIA0KDQpgYGB7cn0NCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIEludm9pY2UgLSBKb2luID0gRGF5cyBkaWZmdGltZSDtlajsiJgg7IKs7JqpIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KYXMubnVtZXJpYyhkaWZmdGltZShjb2hvcnRzMjAxMSRJbnZvaWNlRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgIGNvaG9ydHMyMDExJEpvaW5fRGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgdW5pdHMgPSBjKCJkYXlzIikpKSAtPiBjb2hvcnRzMjAxMSRkYXlzICAjVW5pdCDsp4DsoJU6IOydvCDrs4Qg6rOE7IKwDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIGRheXMvMzAgPSBtb250aCAmIEludm9pY2UgRGF0ZSDsnbzsnpAg7IKt7KCcIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KY29ob3J0czIwMTEgJT4lICANCiAgbXV0YXRlKG1vbnRoID0gcm91bmQoZGF5cy8zMCkpICU+JSANCiAgbXV0YXRlKEludm9pY2VEYXRlID0gZm9ybWF0KEludm9pY2VEYXRlLCAiJVktJW0iKSkgJT4lIA0KICBtdXRhdGUoSm9pbl9EYXRlID0gZm9ybWF0KEpvaW5fRGF0ZSwgIiVZLSVtIikpIC0+IGRmX2NvDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICBDb2hvcnQg7IOd7ISx7ZWY6riwICANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpjICgiSmFuIENvaG9ydCIsIA0KICAgIkZlYiBDb2hvcnQiLA0KICAgIk1hciBDb2hvcnQiLA0KICAgIkFwciBDb2hvcnQiLCANCiAgICJNYXkgQ29ob3J0IiwgDQogICAiSnVuIENvaG9ydCIsDQogICAiSnVsIENvaG9ydCIsDQogICAiQXVnIENvaG9ydCIsDQogICAiU2VwIENvaG9ydCIsDQogICAiT2N0IENvaG9ydCIsDQogICAiTm92IENvaG9ydCIsDQogICAiRGVjIENvaG9ydCIpIC0+IGdyb3Vwcw0KDQoNCg0KZm9yIChpIGluIDE6MTIpIHsNCiAgDQogIGRmX2NvW2RmX2NvJENvaG9ydCA9PSBpLCAiQ29ob3J0Il0gPC0gZ3JvdXBzW2ldDQogIA0KfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIENvaG9ydCBmYWN0b3IgbGV2ZWwg6rOg7KCV6rCSIOyngOyglSAgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmRmX2NvJENvaG9ydCA8LSBmYWN0b3IoZGZfY28kQ29ob3J0LG9yZGVyZWQgPSBULGxldmVscyA9YygiSmFuIENvaG9ydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGZWIgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYXIgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBcHIgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYXkgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJKdW4gQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJKdWwgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdWcgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXAgQ29ob3J0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT2N0IENvaG9ydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vdiBDb2hvcnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWMgQ29ob3J0IikpDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIENvaG9ydCBmYWN0b3Ig67OA7ZmYICANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojc3RyKGNvaG9ydHMyMDExJENvaG9ydCkNCg0Kd2hpY2goZHVwbGljYXRlZChkZl9jb1stNTotNl0pKSAtPiBkdXANCg0KDQpkZl8xIDwtIGRmX2NvWy1kdXAsXQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIO2UhOugiOyehCDrs4DtmZjtlZjquLAgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQpkY2FzdChkZl8xLCBDb2hvcnQgfiBtb250aCwgDQogICAgICB2YWx1ZS52YXIgPSAiQ3VzdG9tZXJJRCIsDQogICAgICBmdW4uYWdncmVnYXRlID0gbGVuZ3RoKSAtPmNvaG9ydHMud2lkZQ0KDQoNCg0KY3cucmV0ZW50aW9uIDwtIGNvaG9ydHMud2lkZQ0KY3cuY2h1cm4gPC1jb2hvcnRzLndpZGUNCg0KDQoNCg0KYnJlYWtzIDwtIHF1YW50aWxlKGNvaG9ydHMud2lkZVssMzoxM10sIHByb2I9IHNlcSguMDUsIC45NSwgLjA1KSwgbmEucm0gPSBUKSANCmNvbG9ycyA8LSBzYXBwbHkocm91bmQoc2VxKDE1NSw4MCwgbGVuZ3RoLm91dCA9IGxlbmd0aChicmVha3MpKzEpLDApLA0KICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KXtyZ2IoeCx4LDE1NSwgbWF4Q29sb3JWYWx1ZSA9IDE1NSl9KQ0KDQoNCg0KDQpkYXRhdGFibGUoY29ob3J0cy53aWRlLA0KICAgICAgICAgICAgICBjbGFzcyA9ICdjZWxsLWJvcmRlciBzdHJpcGUnLA0KICAgICAgICAgICAgIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoDQogICAgICAgICAgICAgICBvcmRlcmluZz1GLA0KICAgICAgICAgICAgICAgZG9tID0gJ3QnLA0KICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDEyKSApICU+JSANCiAgICAgICAgICAgICAgZm9ybWF0U3R5bGUoIjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvciA9ICdsaWdodGdyZXknLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRXZWlnaHQgPSAnYm9sZCcpICU+JQ0KICAgICAgICAgICAgIGZvcm1hdFN0eWxlKG5hbWVzKGNvaG9ydHMud2lkZVtjKC0xLC0yKV0pLGZvbnRXZWlnaHQgPSAnYm9sZCcsY29sb3IgPSAnd2hpdGUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3IgPSBzdHlsZUludGVydmFsKGJyZWFrcyxjb2xvcnMpKQ0KYGBgDQoNCg0KYGBge3J9DQoNCm9ubGluZS5yZXRhaWwzICU+JSAgDQogIGdyb3VwX2J5KEN1c3RvbWVySUQpICU+JSANCiAgbXV0YXRlKE1vbnRoID0gbWluKEludm9pY2VEYXRlKSx1bml0PSJtb250aCIpDQoNCmBgYA0KDQojIFR5cGUgb2YgQ29ob3J0cyANCg0KVGltZSBDb2hvcnRzIDog7KCc7ZKI6rO8IOyEnOu5hOyKpOulvCDsgqzsmqntlZjquLAg7Iuc7J6R7ZWcIOyLnOqwhCDquLDspIAgKE1vbnRoLyBRdWFydGVyIOq4sOykgCkNCg0KQmVoYXZpb3IgQ29ob3J0cyA6IFByb2R1Y3Qg7YOA7J6FIOq4sOykgCAtPiBEZXNpZ24gY3VzdG9tZS1tYWRlIFNlcnZpY2Ugb3IgcHJvZHVjdCDsoITrnrUg7IiY66a9IA0KDQpTaXplIENvaG9ydCA6IOygnO2SiOqzvCDshJzruYTsiqTrpbwg6rWs66ek7ZWcIOqzoOqwneydmCDsgqzsnbTspogg6riw7KSAOiDtirnrs4Qg6riw6rCEIOuPmeyViCDsk7Qg7IaM67mE7JWhIA0KDQpgYGB7cn0NCg0KT25saW5lX1JldGFpbCAtPiByZXRhaWwNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgTkEg7KCc6rGwIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZXRhaWwgJT4lICANCiAgZHJvcF9uYShDdXN0b21lcklEKSAtPiByZXRhaWwNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAg7KSR67O17KCc6rGwIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZXRhaWxbIWR1cGxpY2F0ZWQocmV0YWlsKSxdIC0+cmV0YWlsDQoNCg0Kc3VtbWFyeShyZXRhaWwpDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAwIOydtOyDgeydmCDrjbDsnbTthLDrp4wg7LaU7LacIDogTUlOIOqwkuydtCAwIOyduOqyveyasCDrqqjrjbjrp4Hsl5AgbmVnYXRpdmUgYWZmZWN0LCDsoJzqsbAgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJldGFpbCAlPiUgIA0KICBmaWx0ZXIoUXVhbnRpdHkgPjAgJiBVbml0UHJpY2UgPiAwKSAtPiByZXRhaWwNCmBgYA0KDQojIyBDb2hvcnQgQW5hbHlzaXMgU3RlcCANCg0KMSkgSW52b2ljZSBQZXJpb2QgOiB5ZWFyL21vbnRoIOy2lOy2nCANCg0KMikgQ29ob3J0IEdyb3VwIDogeWVhci9tb250aCDquLDrsJgg6rOg6rCdIOyyqyDqtazrp6Tsnbwg7LaU7LacIA0KDQozKSBDb2hvcnQgcGVyaW9kL0NvaG9ydCBJbmRleCA6IOq1rOunpOydvCDsnbTtm4TsnZggbW9udGgg7J2YIOyImCANCg0KYGBge3J9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBJRCDrs4Qg7LKrIOq1rOunpCDstpTstpwgPSByZXRhaWxfY29ob3J0DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJldGFpbCAlPiUgIA0KICBncm91cF9ieShDdXN0b21lcklEKSAlPiUgDQogIHN1bW1hcmlzZShNb250aCA9IGZsb29yX2RhdGUobWluKEludm9pY2VEYXRlKSwgdW5pdCA9ICJtb250aCIpKSAtPiByZXRhaWxfY29ob3J0DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBJbnZvaWNlRGF0ZSDsl5DshJwgTW9udGgg67OA7IiYIOyDneyEsSA9IHJldGFpbA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZXRhaWwgJT4lIA0KICBtdXRhdGUoSW52b2ljZU1vbnRoID0gZmxvb3JfZGF0ZShJbnZvaWNlRGF0ZSwgdW5pdCA9ICJtb250aCIpKSAtPiByZXRhaWwNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgcmV0YWlsX2NvaG9ydCArIHJldGFpbCDrs5Htlak9IOqwgSBJRCDrs4Qg7LWc7LSIIOq1rOunpCDsnbzsnpDrpbwg642U7ZW07KSA64ukLiANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KbWVyZ2UoIHJldGFpbCwgcmV0YWlsX2NvaG9ydCwNCiAgICAgIGJ5Lng9ICJDdXN0b21lcklEIiwNCiAgICAgIGJ5LnkgPSAiQ3VzdG9tZXJJRCIpIC0+IHJldGFpbF9tZXJnZQ0KDQoNCg0KcmV0YWlsX21lcmdlICU+JSANCiAgbXV0YXRlKEludm9pY2VNb250aF9udW0gPSBtb250aChJbnZvaWNlRGF0ZSksICAj6rWs7J6FIO2VnCDrgqDsnZgg7JuUL+uFhOuPhA0KICAgICAgICAgSW52b2ljZVllYXJfbnVtID0geWVhcihJbnZvaWNlRGF0ZSksDQogICAgICAgICBDb2hvck1vbnRoX251bSA9IG1vbnRoKE1vbnRoKSwgICAgICPqtazsnoUg7LWc7LSIIOydvOydmCDsm5Qv64WEIA0KICAgICAgICAgQ29ob3J0WWVhcl9udW0gPSB5ZWFyKE1vbnRoKSwNCiAgICAgICAgIEluZGV4ID0gKEludm9pY2VZZWFyX251bS1Db2hvcnRZZWFyX251bSkqMTIgKyhJbnZvaWNlTW9udGhfbnVtIC0gQ29ob3JNb250aF9udW0pKzEpIC0+IHJldGFpbF9tZXJnZQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICDstZzstIgg6rWs66ek7J28LCBJbmRleCDquLDspIAgY3VzdG9tZXLsnZgg7IiYIOuIhOyggSDtkZwgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpyZXRhaWxfbWVyZ2UgJT4lIA0KICBncm91cF9ieShNb250aCwgSW5kZXgpICU+JSAj7LWc7LSI6rWs66ek7J28LCBJbmRleA0KICBzdW1tYXJpc2UoVG90YWxfQ3VzdG9tZXIgPSBuX2Rpc3RpbmN0KEN1c3RvbWVySUQpKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBJbmRleCwgdmFsdWVzX2Zyb20gPSBUb3RhbF9DdXN0b21lcikgJT4lIA0KICByZW5hbWUoREVDID0gMiwgDQogICAgICAgICBKQU4gPSAzLCANCiAgICAgICAgIEZFQiA9IDQsIA0KICAgICAgICAgTUFSID0gNSwgDQogICAgICAgICBBUFIgPSA2LA0KICAgICAgICAgTUFZID0gNywgDQogICAgICAgICBKVU4gPSA4LCANCiAgICAgICAgIEpVTCA9IDksIA0KICAgICAgICAgQVVHID0gMTAsIA0KICAgICAgICAgU0VQID0gMTEsIA0KICAgICAgICAgT0NUID0gMTIsIA0KICAgICAgICAgTk9WID0gMTMsDQogICAgICAgICBERUNfQSA9IDE0KSAtPiBjb2hvcnRfY291bnRzDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIOy1nOy0iCDquLDspIAg67mE7JyoIOqzhOyCsCDtkZwgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmNvaG9ydF9jb3VudHMgJT4lICANCiAgbXV0YXRlKFRPVEFMID0gcm91bmQoREVDL0RFQywzKSoxMDAsDQogICAgICAgICBKQU5fUiA9IHJvdW5kKEpBTi9ERUMsMykgKjEwMCwgDQogICAgICAgICBGRUJfUiA9IHJvdW5kKEZFQi9ERUMsMykgKjEwMCwNCiAgICAgICAgIE1BUl9SID0gcm91bmQoTUFSL0RFQywzKSAqMTAwLCANCiAgICAgICAgIEFQUl9SID0gcm91bmQoQVBSL0RFQywzKSAqMTAwLCANCiAgICAgICAgIE1BWV9SID0gcm91bmQoTUFSL0RFQywzKSAqMTAwLCANCiAgICAgICAgIEpVTl9SID0gcm91bmQoSlVOL0RFQywzKSAqMTAwLCANCg0KICAgICAgICAgSlVMX1IgPSByb3VuZChKVUwvREVDLDMpICoxMDAsDQogICAgICAgICBBVUdfUiA9IHJvdW5kKEFVRy9ERUMsMykgKjEwMCwgIA0KICAgICAgICAgU0VQX1IgPSByb3VuZChTRVAvREVDLDMpICoxMDAsDQogICAgICAgICBPQ1RfUiA9IHJvdW5kKE9DVC9ERUMsMykgKjEwMCwgIA0KICAgICAgICAgTk9WX1IgPSByb3VuZChOT1YvREVDLDMpICoxMDAsICANCiAgICAgICAgIERFQ19SID0gcm91bmQoREVDX0EvREVDLDMpICoxMDApICU+JSAgDQogIHNlbGVjdCgtYygyOjE0KSkgLT4gcmV0ZW50aW9uDQoNCnJldGVudGlvbg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIOyLnOqwge2ZlCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KcmV0ZW50aW9uICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKDI6MTQpLCANCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkluZGV4IiwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUmF0ZSIpICU+JSANCiAgbXV0YXRlKEluZGV4ID0gcm93X251bWJlcigpICU+JSBhcy5mYWN0b3IoKSkgJT4lIA0KICBhcnJhbmdlKE1vbnRoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShDb2hvcnRfTW9udGggPSB5bWQoTW9udGgpICU+JSAgI3ltZCDtlajsiJjrpbwg7I2o7JW8IOygnOuMgOuhnCDrgpjsmLQgDQogICAgICAgICAgIGFzLmZhY3RvcigpKSAgLT4gdml6X3JldGVudGlvbg0KDQoNCg0Kdml6X3JldGVudGlvbiAlPiUgDQogIG11dGF0ZShDb2hvcnRfTW9udGggPSBmYWN0b3IoQ29ob3J0X01vbnRoLCBsZXZlbHMgPSByZXYobGV2ZWxzKENvaG9ydF9Nb250aCkpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKEluZGV4LCBDb2hvcnRfTW9udGgpKSsNCiAgZ2VvbV90aWxlKGFlcyhmaWxsPVJhdGUpKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1SYXRlKSxzaXplID0gMikrDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKA0KICAgIGNvbG91cnMgPSBjKCJXaGl0ZSIsICIjZTZkMGExIiwgIiNmZjAwMDAiKSwNCiAgICBuYS52YWx1ZSA9ICJXaGl0ZSIpICsgDQogIGxhYnModGl0bGUgPSJSZXRlbnRpb24gUmF0ZSAoaW4gJSkiKSANCg0KDQoNCmBgYA0KDQojIyBBdmVyYWdlIFF1YW50aXR5IG9mIEVhY2ggQ29ob3J0cyANCg0KDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIO2Pieq3oCDqtazrp6Tsl5Ag64yA7ZWcIENvaG9ydCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnJldGFpbF9tZXJnZSAlPiUgDQogIGdyb3VwX2J5KE1vbnRoLCBJbmRleCkgJT4lIA0KICBzdW1tYXJpc2UoYXZnX3F1YW4gPSByb3VuZChtZWFuKFF1YW50aXR5KSwxKSkgJT4lIA0KICBhcnJhbmdlKE1vbnRoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShDb2hvcnRfTW9udGggPSB5bWQoTW9udGgpICU+JSANCiAgICAgICAgICAgYXMuZmFjdG9yKCksDQogICAgICAgICBJbmRleCA9IGFzLmZhY3RvcihJbmRleCkpICU+JSANCiAgZ2dwbG90KGFlcyh4PSBJbmRleCwgeT1Db2hvcnRfTW9udGgpKSArDQogIGdlb21fdGlsZShhZXMoZmlsbD1hdmdfcXVhbikpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPSBhdmdfcXVhbikpKw0KICBzY2FsZV9maWxsX2dyYWRpZW50bigNCiAgICBjb2xvdXJzID0gYygiV2hpdGUiLCAiIzE4ODUwOCIsICIjMDE2MDAwIiksDQogICAgbmEudmFsdWUgPSBjKCJXaGl0ZSIpKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMgUkZNIHdpdGggTW9kZWxsaW5nIHsudGFic2V0fQ0KDQpSIDog7LWc6re8IA0KDQpGIDog7J6Q7KO8DQoNCk0gOiDquIjslaEgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAg7LSdIOq4iOyVoSDqtaztlZjquLAgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJldGFpbF9tZXJnZSAlPiUgDQogIG11dGF0ZShQdXJjaGFzZSA9IChRdWFudGl0eSAqIFVuaXRQcmljZSkpIC0+IHJldGFpbF9tZXJnZQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICDstZzstIgg6rWs66ek7J28IA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQptYXgocmV0YWlsX21lcmdlJEludm9pY2VEYXRlKQ0KDQpzbmFwc2hvdF9kYXRlPC1tYXgocmV0YWlsX21lcmdlJEludm9pY2VEYXRlKSArIGRheXMoMSkNCnNuYXBzaG90X2RhdGUNCg0KbWluKHJldGFpbF9tZXJnZSRJbnZvaWNlRGF0ZSkNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIFJGTQ0KIyAgIFIgOiDrp4jsp4Drp4kg6rWs66ekIOuCoOynnCAtIOq1rOunpCDrgqDsp5wgDQojICAgRiA6IENvdW50DQojICAgTSA6IFF1YW50aXR5ICogUHJpY2UgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KcmV0YWlsX21lcmdlICU+JSANCiAgZ3JvdXBfYnkoQ3VzdG9tZXJJRCkgJT4lIA0KICBzdW1tYXJpc2UoUmVjZW5jeSA9IGFzLm51bWVyaWMocm91bmQoc25hcHNob3RfZGF0ZS1tYXgoSW52b2ljZURhdGUpLDApKSwNCiAgICAgICAgIEZyZXF1ZW5jeSA9IG4oKSwNCiAgICAgICAgIE1vbmV0YXJ5ID0gc3VtKFB1cmNoYXNlKSkgLT4gcmZtDQoNCg0KaGVhZChyZm0pDQpgYGANCg0KDQpgYGB7cn0NCiNudGlsZSgpIO2VqOyImOuKlCDrtoTsnITsiJjrpbwg6rOE7IKw7ZW07KO866mwLCBuIOyduOyekOulvCDthrXtlbQg66qHIOu2hOychOuhnCDrgpjriIzsp4Ag7ISg7YOd7ZWgIOyImCDsnojsirXri4jri6QuIO2VtOuLuSDtlajsiJgg7Jet7IucICPsmKTrpoTssKjsiJzsnLzroZwg67aE7JyE7IiY66W8IOuCmOuIleuLiOuLpA0KDQpyZm0gJT4lIA0KICBtdXRhdGUoUiA9IG50aWxlKC1SZWNlbmN5LDQpLA0KICAgICAgICAgRnIgPSBudGlsZShGcmVxdWVuY3ksNCksDQogICAgICAgICBNID0gbnRpbGUoTW9uZXRhcnksNCksDQogICAgICAgICByZm1fc2NvcmU9IFIrRnIrTSkgLT4gcmZtDQoNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIEdvbGQvU2xpdmVyL0Jyb256ZSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQppZmVsc2UocmZtJHJmbV9zY29yZSA+IDkgLCAiR29sZCIsIA0KICAgICAgIGlmZWxzZShyZm0kcmZtX3Njb3JlID4gNSAgJiByZm0kcmZtX3Njb3JlIDw9OSwgIlNsaXZlciIsICJCcm9uemUiKSkgLT5yZm0kc2VnbWVudA0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAg6rCBIHNlZ2VtZW50IHJmbSDtj4nqt6ANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQpyZm0gJT4lIA0KICBncm91cF9ieShzZWdtZW50KSAlPiUgDQogIHN1bW1hcmlzZShSZWNlbmN5ID0gbWVhbihSZWNlbmN5KSwNCiAgICAgICAgICAgIEZyZXF1ZW5jeSA9IG1lYW4oRnJlcXVlbmN5KSwNCiAgICAgICAgICAgIE1vbmV0YXJ5ID0gbWVhbihNb25ldGFyeSkpICU+JSANCiAgYXJyYW5nZShkZXNjKFJlY2VuY3kpKSAtPnJmbS5zZWcNCmBgYA0KDQoNCiMjIEtNRUFOUyANCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgU3RlcCAxKSBTY2FsaW5nIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0Kc2NhbGUocmZtWywyOjRdKSAtPiByZm1fc2NhbGUNCg0KDQoNCmRpc3QocmZtX3NjYWxlLCBtZXRob2QgPSJldWNsaWRlYW4iKSAlPiUgDQogIGFzLm1hdHJpeCgpIC0+IHJmbS5kaXN0DQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBTdGVwIDIpIEsg6rCv7IiYICA9IDPqsJzroZwg7ISk7KCVDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQpmdml6X25iY2x1c3QocmZtX3NjYWxlLA0KICAgICAgICAgICAgIGttZWFucywgDQogICAgICAgICAgICAgbWV0aG9kID0gIndzcyIsDQogICAgICAgICAgICAgay5tYXg9OSkNCg0KDQpmdml6X25iY2x1c3QocmZtX3NjYWxlLA0KICAgICAgICAgICAgIGttZWFucywgDQogICAgICAgICAgICAgbWV0aG9kID0gInNpbGhvdWV0dGUiLA0KICAgICAgICAgICAgIGsubWF4PTkpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBTdGVwIDMpIEttZWFuIGNsdXN0ZXJpbmcgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQprbWVhbnMocmZtX3NjYWxlLCANCiAgICAgICBjZW50ZXJzID0gMywNCiAgICAgICBpdGVyLm1heCA9IDEwMDApIC0+IHJmbS5rbWVhbnMNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBTdGVwIDQpIOq1sOynkeuzhCDtj4nqt6Ag6rCSIO2ZleyduCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZm0ua21lYW5zJGNlbnRlcnMNCg0KYmFycGxvdCh0KHJmbS5rbWVhbnMkY2VudGVycyksIGJlc2lkZSA9IFQsIGNvbD0yOjQpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBTdGVwIDUpIGNsdXN0ZXIg7ZWg64u5IA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmFzLmZhY3RvcihyZm0ua21lYW5zJGNsdXN0ZXIpIC0+IHJmbSRjbHVzdGVyX2ttZWFuDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICAgU3RlcCA2KSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIGVhY2ggY2x1c3Rlcg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJmbSAlPiUgIA0KICBncm91cF9ieShjbHVzdGVyX2ttZWFuKSAlPiUgDQogIHN1bW1hcmlzZShSID0gbWVhbihSZWNlbmN5KSwNCiAgICAgICAgICAgIEYgPSBtZWFuKEZyZXF1ZW5jeSksDQogICAgICAgICAgICBNID0gbWVhbihNb25ldGFyeSkpICU+JSANCiAgYXJyYW5nZShkZXNjKFIpKSAtPiByZm1fa21lYW5zDQoNCmBgYA0KDQojIyBLLU1lb2lkIA0KDQpgYGB7cn0NCg0KDQpzZXQuc2VlZCgyMDIxKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIEstbWVkb2lkIOyXkCDrtoDtlantlZjripQgSyDqsK/siJgg6rWs7ZWY6riwIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpmdml6X25iY2x1c3QocmZtX3NjYWxlLCANCiAgICAgICAgICAgICBwYW0sIA0KICAgICAgICAgICAgIG1ldGhvZCA9ICJ3c3MiLA0KICAgICAgICAgICAgIGsubWF4PTkpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBLLW1lZG9pZCDsi6TtlokgcGFtIO2VqOyImCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KcGFtKHJmbV9zY2FsZSwgaz0zKSAtPnJmbS5rbWVkb2lkDQoNCg0KcGxvdChyZm0ua21lZG9pZCkNCg0KDQpiYXJwbG90KHQocmZtLmttZWRvaWQkbWVkb2lkcyksYmVzaWRlID0gVCwgY29sPTI6NCkNCmxlZ2VuZCgidG9wcmlnaHQiLCBjb2xuYW1lcyhyZm1fc2NhbGUpLCBmaWxsPTI6MTQsIGNleD0wLjgpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBDbHVzdGVyaW5nIO2VoOuLuSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQphcy5mYWN0b3IocmZtLmttZWRvaWQkY2x1c3RlcmluZykgLT4gcmZtJG1lZG9pZF9jbHVzdGVyDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgICBDaGFyYWN0ZXJpc3RpYyBmb3IgZWFjaCBLLW1lZG9pZHMgY2x1c3RlcnMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyZm0gJT4lIA0KICBncm91cF9ieShtZWRvaWRfY2x1c3RlcikgJT4lIA0KICBzdW1tYXJpc2UoUiA9IHJvdW5kKG1lYW4oUmVjZW5jeSksMiksDQogICAgICAgICAgICBGID0gcm91bmQobWVhbihGcmVxdWVuY3kpLDIpLA0KICAgICAgICAgICAgTSA9IHJvdW5kKG1lYW4oTW9uZXRhcnkpLDIpKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhSKSkgLT4gcmZtX21lZA0KDQpEVDo6ZGF0YXRhYmxlKHJmbV9tZWQpDQoNCmBgYA0KDQoNCkNsdXN0ZXIgMSA6IFJlY2VuY3kg6rCAIDE4MCDsnbTsg4HsnLzroZwsIDbqsJzsm5Qg64+Z7JWIIOq1rOunpCDrgrTsl63snbQg7JeG64qUIOq3uOujueydtOuLpC4g67CY66m0IOu5iOuPhOuKlCAyNSwg7Y+J6regIOq1rOunpOyVoeydgCBHQlA1OTQNCkNsdXN0ZXIgMiA6IFJlY25lY3kg6rCAIDk4IOycvOuhnCAz6rCc7JuUIOuPmeyViCDqtazrp6Qg64K07Jet7J20IOyXhuuKlCDqt7jro7nsnbTri6QuIOu5iOuPhOyImOuKlCA0MeycvOuhnCwg7Y+J6regIDg2MSDsnYQg6rWs66ek7ZaI64ukLiANCkNsdXN0ZXIgMyA6IFJlY2VuY3kg6rCAIDIyIOycvOuhnCDrp4jsp4Drp4kg6rWs66ek7J2866GcIOu2gO2EsCwgMzDsnbwg7J2064K0IOq1rOunpOOFjuulvCDtlZwg6re466O57Jy866GcIDE0MiDruYjrj4QsIO2Pieq3oCAzMjUwIEdCUCDsgqzsmqkgDQoNCg0KDQoNCg0K