Chapter 3: 왜 매상이 감소하는가?

STEP 1 < 현재의 Fact 와 원하는 Ideal 케이스에 대한 정의를 간단하게 내린다 >

  현재: 매출이 떨어짐 
  해결: 지난달과 비슷한 매출을 확보해야한다 
  

STEP 2 < 매출 감소의 원인에 대해 사실에 기반한 가설을 세워 보자 >

최종가설: 매출의 감소는 신규 회원들이 광고를 안 봐서이다. (그럼 신규 회원들이 돈은 냈나?)

  매출의 감소 원인 1) 광고에 문제가 생김 
                   2) 매월 일관성이 없는 게임의 이벤트에 문제가 있나? 
                   
  사실관계파악 (정보수집) : 1) 광고에 문제가 생겼다 - 알고보니 돈이 없어서 많이 못함 
                            2) 일관성 문제 - 이벤트 내용은 딱히 달라진게 없었다. 
                            
  !! 광고에 문제가 생겨서 매출이 떨어졌다는 가설에 힘이 실린다. 
     돈이 없어서 광고가 많이 안 됐으니까, 홍보가 제대로 이루어지지 않은 문제인거 같다. 
     신규 회원이 늘지 못한 것일수도 있고, 기존 유저가 새로운 이벤트를 시도하지 않은 것일 수도 있다. 
     
     신규 회원의 감소 (= 매출이 증가하지 않음) 과 매출의 감소의 관계를 데이터로 확인해본다. 
     

STEP 3 <어떤 데이터를 봐야하나?>

  문제 * 매출이 떨어졌다. 
       * 광고를 적게 했다. 
       * 그래서 신규 유저가 줄었다. (가설)
       
  해결책 * 광고비를 더 투자한다. 
  
  DAU DATA : 하루에 한 번이상 게임을 이용한 유저 
         
          - log_date :로그인 한 날 
          - 앱 이름 : app_name 
          - 유저 : user_id 
          
  DPU DATA : 하루에 1원 이상 돈을 낸 유저 
  
          - 과금일 : log_date
          - 앱 이름 : app_name 
          - 유저 ID : user_id
          - 과금액 : payment 
          
  INSTALL DATA : 설치 시작 날짜 (즉, 신규회원)
  
          -이용 시작한 날: install_date
          -앱 이름 ; app_name 
          - 유저 : user_id 
          

DATA 이해하기 DAU DATA (Daily Active User)

2013-06-01 날 game-01 앱을 사용한 사람은 id 116 이다. 
>> 접속한 사람들 내역 확인 

DPU DATA (Daily Payment User)

2013-06-01 날 game-01 앱에서 결제한 사람의 id 는 351 이다. 
같은 사람이 같은 날에 여러번 들어와서 게임한 경우에는 그때마다 과금액 데이터가 남는다. 

Install Data

게임을 시작한 유저들 

데이터 전처리 하기

  1. DAU + Install : 신규로 앱을 설치해서 하루에 한번 이상 게임을 이용한 유저들이 있는지 확인

  2. DPU + : 들어와서 과금한 유저가 있을까?

Logical Flow : 하루에 한 번 이상 게임을 한 사람이 (DAU) 새로운 사람인가? (Install) 그 유저들이 있다면 돈을 냈나? (DPU)

< Payment NA 의 이해 >

log_date 는 들어와서 돈을 낸 날짜 install_date 는 앱을 설치한 날짜

즉, 신규로 들어와서 설치를 했는데 돈을 낸 적이 없다는 것.

  1. 비과금 유저의 과금액 0 넣기 (NA= payment)
##     log_date user_id app_name install_date payment
## 1 2013-06-01       1  game-01   2013-04-15       0
## 2 2013-06-01       3  game-01   2013-04-15       0
## 3 2013-06-01       6  game-01   2013-04-15       0
## 4 2013-06-01      11  game-01   2013-04-15       0
## 5 2013-06-01      17  game-01   2013-04-15       0
## 6 2013-06-01      18  game-01   2013-04-15       0
  1. 월차로 집계하기

매월 유저별로 집계한 데이터로 변환 시킨다. -> 월별로

1) 월 만 빼낸다 substr 함수 사용 (log_date, install_date)
2) 월 별 payment 합계를 본다 
##     log_date user_id app_name install_date payment log_month install_month
## 1 2013-06-01       1  game-01   2013-04-15       0   2013-06       2013-04
## 2 2013-06-01       3  game-01   2013-04-15       0   2013-06       2013-04
## 3 2013-06-01       6  game-01   2013-04-15       0   2013-06       2013-04
## 4 2013-06-01      11  game-01   2013-04-15       0   2013-06       2013-04
## 5 2013-06-01      17  game-01   2013-04-15       0   2013-06       2013-04
## 6 2013-06-01      18  game-01   2013-04-15       0   2013-06       2013-04
  1. 신규/유저를 구분하는 항목 추가하기

설치한 달과 들어온 달이 같으면 = 신규(install), 아니면 기존 고객 (existing)

## # A tibble: 6 x 5
## # Groups:   log_month, user_id [6]
##   log_month user_id install_month payment use_type
##   <chr>       <int> <chr>           <dbl> <chr>   
## 1 2013-06         1 2013-04             0 existing
## 2 2013-06         2 2013-04             0 existing
## 3 2013-06         3 2013-04        149940 existing
## 4 2013-06         4 2013-04             0 existing
## 5 2013-06         6 2013-04             0 existing
## 6 2013-06         7 2013-04             0 existing
  1. 그래프 시각화

##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##      0.0      0.0      0.0    160.8      0.0 208610.0

Conclustion

1. 매상이 떨어졌다 (사실) 
2. 신규 유저의 매상이 감소했고,그 중에서 20,000원 이하의 라이트 유저의 감소가 컸다 (사실)
3. 광고 비용을 떨어트리면 안된다. 

Chapter 4 : 이탈 고객 분석 (탈퇴하는 이유를 분석) - 크로스 집계

  1. USER 의 수가 감소 했음

원인에 대한 가설 1) 광고 때문에 탈퇴 유저수가 더 많다. - 광고는 지난 달이랑 비슷했고, 신규 유저도 있다. 2) 매월 테마를 바꿔서 개최하던 이벤트가 식상해졌다. - 이벤트 내용도 거의 바뀌지 않았다. 3) 특정 고객층의 이탈이 있다. - 유력 가설

원인을 제대로 알 수 없기 때문에 ‘탐색형’ 데이터 분석을 실시행

  • 크로스 집계를 사용하여 세그먼트 도출 분석
  1. 데이터 선정

    DAU - 1 회 이상 방문 데이터

      - log_date, app_name, id 

    USER.INFO - 사용자 고객 정보 관련 데이터

      - install_date, app_name, id, gender, generation, device_type 
  1. Gender, Generation, Device_type 별 집계하기

3.1 Gender

Gender 별 방문에 대한 집계 이기 때문에, log_month 를 집계한다.

## # A tibble: 4 x 3
## # Groups:   log_month [2]
##   log_month gender     n
##   <chr>     <fct>  <int>
## 1 2013-08   F      47343
## 2 2013-08   M      46842
## 3 2013-09   F      38027
## 4 2013-09   M      38148
##          gender
## log_month     F     M
##   2013-08 47343 46842
##   2013-09 38027 38148

table 함수로 Cross 교차 집계 - 특징: table 함수가 두 factor 의 집계를 보기엔 좋다.

성별에 따른 방문 유저의 수가 준것 같지 않다.

3.2 Generation

##          generation
## log_month    10    20    30    40    50
##   2013-08 18785 33671 28072  8828  4829
##   2013-09 15391 27229 22226  7494  3835

연령 별 차이도 없는 것 같아 보인다.

3.3 Gender x Generation

dcast 함수 사용 : 두 factor 형 크로스 집계 사용

##   log_month F_10  F_20  F_30 F_40 F_50 M_10  M_20  M_30 M_40 M_50
## 1   2013-08 9091 17181 14217 4597 2257 9694 16490 13855 4231 2572
## 2   2013-09 7316 13616 11458 3856 1781 8075 13613 10768 3638 2054

성별 + 나이에서도 뚜렷하게 이탈이 일어난 것 같지 않아 보인다.

3.4 Device

##          device_type
## log_month Android   iOS
##   2013-08   46974 47211
##   2013-09   29647 46528

차이가 나타남

A/B Test

A/B Test 란, 웹 사이트 방문자를 임의의 두 집단으로 나누고, A 집단에게는 기존 사이트를, B 집단에게는 새로운 사이트를 보여준 다음, 두 집단 중 어떤 집단이 더 높은 성과를 보여주는지에 대해 평가하는 방식이다.

성과 기준은 : 회원 가입률, 재방문율, 구매전환률 등의 지표를 확인한다.

인과 관계를 찾기 위한 테스트

주의 할 점 : 임의적 할당 (Random Assignment)

 그룹 A/B를 남/여 혹은 짝수/홀수, 첫 일주일 방문 자/그 다음 일주일 방문자      등 임의적이지 않은 방식으로 구분 하는 경우, 두 집단의 차이가 무엇            때문인지 가려낼 수가 없다. 
 
 임의적 추출 예시) 초등학교 학생들을 대상으로 한 실험의 결과를 초중고         학생에게 모두 적용하거나, 페이스 북 사용자를 대상의 실험을 트위터에          적용시키거나 하면 집단의 성격과 모집단의 성격에 차이가 있기 때문에           기대와 다른 결과를 나타낼 수 있다. 
 
 
 1) 전후 비교 X 
   Before/ After 로 확인하면, 외부 요인을 배제 할 수 없다. 
   
   예를 들어, 배너 B를 광고하는 데 전체 구매율이 좋았다. 
              배너 B를 광고하는데 다른 이벤트가 대박을 쳤다. 
              배너 B를 광고하는데 방송에 소개되었다. 
              
   >> 외부요인이 작용하였기 때문에, 그렇다면 A를 내걸었어도 좋았겠네 
      그건 아무도 모름 

 2) 통계적 가설 T.TEST 는 커트라인으로만 바라보기 
 
 적어도 가설 검정에서는 의미가 있는 차이가 나타났으므로 비지니스 상에서
 의미가 있는 차이인지 검토를 해봐야한다. 

데이터 설명 < 배너 광고의 표시 획수 정보> log_date : 표시 날짜 test_name : test 이름 test_case : A, B user_id transcation_id : 배너 광고가 표시되었을 때 발생하는 id

데이터 설명 <배너 광고의 클릿 횟수 정보> log_date : 클릭한 날짜 test_name : test 이름 test_case : A, B user_id transcation_id : 배너 광고가 표시되었을 때 발생하는 id

Transcation_id 기준 : 배너광고가 표시되었을때 발생하는 id 로 표시로그와 클릭 로그를 결합할 키로 사용

즉, 표시와 클릭 기록이 맞는 기준으로 병합

##   transaction_id   log_date app_name  test_name test_case user_id log_date.g
## 1              1 2013-10-02  game-01 sales_test         A   49017       <NA>
## 2              2 2013-10-02  game-01 sales_test         B   49018       <NA>
## 3              3 2013-10-02  game-01 sales_test         A   44338       <NA>
## 4              4 2013-10-02  game-01 sales_test         A   44339       <NA>
## 5              5 2013-10-02  game-01 sales_test         A   28598       <NA>
## 6              6 2013-10-02  game-01 sales_test         B   30306       <NA>
##   app_name.g test_name.g test_case.g user_id.g
## 1       <NA>        <NA>        <NA>        NA
## 2       <NA>        <NA>        <NA>        NA
## 3       <NA>        <NA>        <NA>        NA
## 4       <NA>        <NA>        <NA>        NA
## 5       <NA>        <NA>        <NA>        NA
## 6       <NA>        <NA>        <NA>        NA
##    transaction_id   log_date app_name  test_name test_case user_id log_date.g
## 36             36 2013-10-02  game-01 sales_test         B   35315 2013-10-02
##    app_name.g test_name.g test_case.g user_id.g
## 36    game-01  sales_test           B     35315

배너 광고 B가 user_id 35315 에게 표시 된 날짜가 10월 2일 이였는데, 해당 유저는 B를 클릭했다. 이 transcation_id 는 36 번이다.

클릭 유무 플래그 작성하기

User_id.g 는 클릭을 한 아이디를 나타내주기 때문에 NA 이면 클릭을 안 했다는 것

클릭률 집계하기

## # A tibble: 2 x 2
##   test_case    cvr
## * <fct>      <dbl>
## 1 A         0.0803
## 2 B         0.115

x2 실행하기

## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  ab.test.imp$test_case and ab.test.imp$is_goal
## X-squared = 308.38, df = 1, p-value < 2.2e-16

테스트 케이스별 클릭율 산출하기

  1. 날짜, 테스트 케이스 별 산출
  1. 시각화

Chapter 5 : A/B Test -2

A.B Test : 방문자 수가 비슷한 두 개의 웹 페이지를 비교

일반적으로 새롭게 도입하는 기능이 의도한 바대로 효과가 있을지 확인하기 위해서이다.

CASE STUDY - 두 개의 호텔 예약 사이트 A/B 테스트

Data Summary

  1. Variance A : 기존의 제품 혹은 기능을 묘사하는 통제 집단

  2. Variance B : 새로운 제품 혹은 기능을 묘사하여, 사용자가 좋아하는지 예약 수가 늘었는지 확인하는 예약 집단

  3. Converted - 전환률 T/F

  4. 가설 설정

Ho: A (기존), B(신규) 집단 사이의 전환률은 같다. (= 효과가 없다)

H1: 효과가 있다.

## [1] 20
## [1] 721
## [1] 0.02773925
## [1] 0.05068493
## [1] 82.71918

B 가 A 보다 82% 더 높다.

Pooled Probability for Test Versions A & B

## [1] 5.404837e-05
LS0tDQp0aXRsZTogIkJ1c2luZXNzIENhc2UiDQphdXRob3I6ICJET0VVTiINCmRhdGU6ICIwMi8wMi8yMDIxIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4NCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCmBgYHtyfQ0KI2xpYnJhcnkoRFQpDQoNCg0KI2xpYnJhcnkoa25pdHIpDQojbGlicmFyeShybWFya2Rvd24pDQojbGlicmFyeShtYXJrZG93bikNCmBgYA0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgY2FjaGUgPSBUUlVFLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTApDQpgYGANCg0KDQojIENoYXB0ZXIgMzog7JmcIOunpOyDgeydtCDqsJDshoztlZjripTqsIA/IA0KDQpTVEVQIDEgPCDtmITsnqzsnZggRmFjdCDsmYAg7JuQ7ZWY64qUIElkZWFsIOy8gOydtOyKpOyXkCDrjIDtlZwg7KCV7J2Y66W8IOqwhOuLqO2VmOqyjCDrgrTrprDri6QgPiANCg0KICAgICAg7ZiE7J6sOiDrp6TstpzsnbQg65ao7Ja07KeQIA0KICAgICAg7ZW06rKwOiDsp4Drgpzri6zqs7wg67mE7Iq37ZWcIOunpOy2nOydhCDtmZXrs7TtlbTslbztlZzri6QgDQogICAgICANClNURVAgMiA8IOunpOy2nCDqsJDshozsnZgg7JuQ7J247JeQIOuMgO2VtCDsgqzsi6Tsl5Ag6riw67CY7ZWcIOqwgOyEpOydhCDshLjsm4wg67O07J6QID4gDQoNCiAg7LWc7KKF6rCA7ISkOiDrp6TstpzsnZgg6rCQ7IaM64qUIOyLoOq3nCDtmozsm5Drk6TsnbQg6rSR6rOg66W8IOyViCDrtJDshJzsnbTri6QuICjqt7jrn7wg7Iug6recIO2ajOybkOuTpOydtCDrj4jsnYAg64OI64KYPykNCg0KICAgICAg66ek7Lac7J2YIOqwkOyGjCDsm5DsnbggMSkg6rSR6rOg7JeQIOusuOygnOqwgCDsg53quYAgDQogICAgICAgICAgICAgICAgICAgICAgIDIpIOunpOyblCDsnbzqtIDshLHsnbQg7JeG64qUIOqyjOyehOydmCDsnbTrsqTtirjsl5Ag66y47KCc6rCAIOyeiOuCmD8gDQogICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAg7IKs7Iuk6rSA6rOE7YyM7JWFICjsoJXrs7TsiJjsp5EpIDogMSkg6rSR6rOg7JeQIOusuOygnOqwgCDsg53qsrzri6QgLSDslYzqs6Drs7Tri4gg64+I7J20IOyXhuyWtOyEnCDrp47snbQg66q77ZWoIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyKSDsnbzqtIDshLEg66y47KCcIC0g7J2067Kk7Yq4IOuCtOyaqeydgCDrlLHtnogg64us65287KeE6rKMIOyXhuyXiOuLpC4gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgISEg6rSR6rOg7JeQIOusuOygnOqwgCDsg53qsqjshJwg66ek7Lac7J20IOuWqOyWtOyhjOuLpOuKlCDqsIDshKTsl5Ag7Z6Y7J20IOyLpOumsOuLpC4gDQogICAgICAgICDrj4jsnbQg7JeG7Ja07IScIOq0keqzoOqwgCDrp47snbQg7JWIIOuQkOycvOuLiOq5jCwg7ZmN67O06rCAIOygnOuMgOuhnCDsnbTro6jslrTsp4Dsp4Ag7JWK7J2AIOusuOygnOyduOqxsCDqsJnri6QuIA0KICAgICAgICAg7Iug6recIO2ajOybkOydtCDripjsp4Ag66q77ZWcIOqyg+ydvOyImOuPhCDsnojqs6AsIOq4sOyhtCDsnKDsoIDqsIAg7IOI66Gc7Jq0IOydtOuypO2KuOulvCDsi5zrj4TtlZjsp4Ag7JWK7J2AIOqyg+ydvCDsiJjrj4Qg7J6I64ukLiANCiAgICAgICAgIA0KICAgICAgICAg7Iug6recIO2ajOybkOydmCDqsJDshowgKD0g66ek7Lac7J20IOymneqwgO2VmOyngCDslYrsnYwpIOqzvCDrp6TstpzsnZgg6rCQ7IaM7J2YIOq0gOqzhOulvCDrjbDsnbTthLDroZwg7ZmV7J247ZW067O464ukLiANCiAgICAgICAgIA0KU1RFUCAzIDzslrTrlqQg642w7J207YSw66W8IOu0kOyVvO2VmOuCmD8+IA0KDQoNCiAgICAgIOusuOygnCAqIOunpOy2nOydtCDrlqjslrTsoYzri6QuIA0KICAgICAgICAgICAqIOq0keqzoOulvCDsoIHqsowg7ZaI64ukLiANCiAgICAgICAgICAgKiDqt7jrnpjshJwg7Iug6recIOycoOyggOqwgCDspITsl4jri6QuICjqsIDshKQpDQogICAgICAgICAgIA0KICAgICAg7ZW06rKw7LGFICog6rSR6rOg67mE66W8IOuNlCDtiKzsnpDtlZzri6QuIA0KICAgICAgDQogICAgICBEQVUgREFUQSA6IO2VmOujqOyXkCDtlZwg67KI7J207IOBIOqyjOyehOydhCDsnbTsmqntlZwg7Jyg7KCAIA0KICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAtIGxvZ19kYXRlIDrroZzqt7jsnbgg7ZWcIOuCoCANCiAgICAgICAgICAgICAgLSDslbEg7J2066aEIDogYXBwX25hbWUgDQogICAgICAgICAgICAgIC0g7Jyg7KCAIDogdXNlcl9pZCANCiAgICAgICAgICAgICAgDQogICAgICBEUFUgREFUQSA6IO2VmOujqOyXkCAx7JuQIOydtOyDgSDrj4jsnYQg64K4IOycoOyggCANCiAgICAgIA0KICAgICAgICAgICAgICAtIOqzvOq4iOydvCA6IGxvZ19kYXRlDQogICAgICAgICAgICAgIC0g7JWxIOydtOumhCA6IGFwcF9uYW1lIA0KICAgICAgICAgICAgICAtIOycoOyggCBJRCA6IHVzZXJfaWQNCiAgICAgICAgICAgICAgLSDqs7zquIjslaEgOiBwYXltZW50IA0KICAgICAgICAgICAgICANCiAgICAgIElOU1RBTEwgREFUQSA6IOyEpOy5mCDsi5zsnpEg64Kg7KecICjspoksIOyLoOq3nO2ajOybkCkNCiAgICAgIA0KICAgICAgICAgICAgICAt7J207JqpIOyLnOyeke2VnCDrgqA6IGluc3RhbGxfZGF0ZQ0KICAgICAgICAgICAgICAt7JWxIOydtOumhCA7IGFwcF9uYW1lIA0KICAgICAgICAgICAgICAtIOycoOyggCA6IHVzZXJfaWQgDQogICAgICAgICAgICAgIA0KDQoNCmBgYHtyIGNhcnN9DQoNCnNldHdkKCdDOi9Vc2Vycy9BZG1pbmlzdHJhdG9yL0Rlc2t0b3AvUiBBbmFseXNpcy9CdXNpbmVzcyBSIEFhbmx5c2lzIHNvdXJjZS9zb3VyY2UvRGF0YUFuYWx5c2lzX3NyYy9SJykNCg0KcmVhZC5jc3YoInNlY3Rpb24zLWRhdS5jc3YiLCBoZWFkZXIgPSBULCBzdHJpbmdzQXNGYWN0b3JzID0gVCkgLT4gZGF1IA0KcmVhZC5jc3YoInNlY3Rpb24zLWRwdS5jc3YiLCBoZWFkZXIgPSBULCBzdHJpbmdzQXNGYWN0b3JzID0gVCkgLT4gZHB1IA0KcmVhZC5jc3YoInNlY3Rpb24zLWluc3RhbGwuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IFQpIC0+IGluc3RhbGwNCg0KYGBgDQoNCiBEQVRBIOydtO2VtO2VmOq4sCANCiBEQVUgREFUQSAoRGFpbHkgQWN0aXZlIFVzZXIpDQoNCiAgICAyMDEzLTA2LTAxIOuCoCBnYW1lLTAxIOyVseydhCDsgqzsmqntlZwg7IKs656M7J2AIGlkIDExNiDsnbTri6QuIA0KICAgID4+IOygkeyGje2VnCDsgqzrnozrk6Qg64K07JetIO2ZleyduCANCg0KYGBge3IgcHJlc3N1cmUsIGVjaG89RkFMU0V9DQoNCkRUOjogZGF0YXRhYmxlKGRhdSkNCmBgYA0KDQoNCkRQVSBEQVRBIChEYWlseSBQYXltZW50IFVzZXIpDQoNCiAgICAyMDEzLTA2LTAxIOuCoCBnYW1lLTAxIOyVseyXkOyEnCDqsrDsoJztlZwg7IKs656M7J2YIGlkIOuKlCAzNTEg7J2064ukLiANCiAgICDqsJnsnYAg7IKs656M7J20IOqwmeydgCDrgqDsl5Ag7Jes65+s67KIIOuTpOyWtOyZgOyEnCDqsozsnoTtlZwg6rK97Jqw7JeQ64qUIOq3uOuVjOuniOuLpCDqs7zquIjslaEg642w7J207YSw6rCAIOuCqOuKlOuLpC4gDQoNCmBgYHtyfQ0KDQpEVDo6IGRhdGF0YWJsZShkcHUpDQpgYGANCg0KIEluc3RhbGwgRGF0YSANCg0KICAgIOqyjOyehOydhCDsi5zsnpHtlZwg7Jyg7KCA65OkIA0KDQpgYGB7cn0NCg0KRFQ6OiBkYXRhdGFibGUoaW5zdGFsbCkNCg0KYGBgDQoNCiDrjbDsnbTthLAg7KCE7LKY66asIO2VmOq4sCANCg0KMS4gREFVICsgSW5zdGFsbCA6IOyLoOq3nOuhnCDslbHsnYQg7ISk7LmY7ZW07IScIO2VmOujqOyXkCDtlZzrsogg7J207IOBIOqyjOyehOydhCDsnbTsmqntlZwg7Jyg7KCA65Ok7J20IOyeiOuKlOyngCDtmZXsnbggDQoNCjIuIERQVSArIDog65Ok7Ja07JmA7IScIOqzvOq4iO2VnCDsnKDsoIDqsIAg7J6I7J2E6rmMPyANCg0KTG9naWNhbCBGbG93IDog7ZWY66Oo7JeQIO2VnCDrsogg7J207IOBIOqyjOyehOydhCDtlZwg7IKs656M7J20IChEQVUpDQogICAgICAgICAgICAgICDsg4jroZzsmrQg7IKs656M7J246rCAPyAoSW5zdGFsbCkNCiAgICAgICAgICAgICAgIOq3uCDsnKDsoIDrk6TsnbQg7J6I64uk66m0IOuPiOydhCDrg4jrgpg/IChEUFUpDQoNCg0KYGBge3J9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIERBVSArIElOU1RBTEwgPSBkYXVfaW5zdGFsbCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQptZXJnZShkYXUsIGluc3RhbGwsIGJ5ID1jKCJ1c2VyX2lkIiwgImFwcF9uYW1lIikpIC0+ZGF1X2luc3RhbGwNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICBEQVUgKyBJTlNUQUxMID0gZGF1X2luc3RhbGwgKyBwYXltZW50ID0gZGF1X2luc3RhbGxfcGF5bWVudCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQptZXJnZShkYXVfaW5zdGFsbCwgZHB1LCBieT0gYygibG9nX2RhdGUiICwgInVzZXJfaWQiLCAiYXBwX25hbWUiKSwgYWxsLnggPSBUICkgLT4gZGF1X2luc3RhbGxfcGF5bWVudCANCg0KDQpkYXRhdGFibGUoZGF1X2luc3RhbGxfcGF5bWVudCkNCmBgYA0KDQo8IFBheW1lbnQgTkEg7J2YIOydtO2VtCA+IA0KICANCmxvZ19kYXRlIOuKlCDrk6TslrTsmYDshJwg64+I7J2EIOuCuCDrgqDsp5wgDQppbnN0YWxsX2RhdGUg64qUIOyVseydhCDshKTsuZjtlZwg64Kg7KecIA0KDQrspoksIOyLoOq3nOuhnCDrk6TslrTsmYDshJwg7ISk7LmY66W8IO2WiOuKlOuNsCDrj4jsnYQg64K4IOyggeydtCDsl4bri6TripQg6rKDLiANCg0KMy4g67mE6rO86riIIOycoOyggOydmCDqs7zquIjslaEgMCDrhKPquLAgKE5BPSBwYXltZW50KQ0KDQpgYGB7cn0NCg0KZGF1X2luc3RhbGxfcGF5bWVudCRwYXltZW50W2lzLm5hKGRhdV9pbnN0YWxsX3BheW1lbnQkcGF5bWVudCldIDwtIDAgDQoNCmhlYWQoZGF1X2luc3RhbGxfcGF5bWVudCkNCmBgYA0KDQo0LiDsm5TssKjroZwg7KeR6rOE7ZWY6riwIA0KDQrrp6Tsm5Qg7Jyg7KCA67OE66GcIOynkeqzhO2VnCDrjbDsnbTthLDroZwg67OA7ZmYIOyLnO2CqOuLpC4gLT4g7JuU67OE66GcIA0KDQogICAgMSkg7JuUIOunjCDrubzrgrjri6Qgc3Vic3RyIO2VqOyImCDsgqzsmqkgKGxvZ19kYXRlLCBpbnN0YWxsX2RhdGUpDQogICAgMikg7JuUIOuzhCBwYXltZW50IO2VqeqzhOulvCDrs7jri6QgDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgTG9nX21vbnRoIOyblCDstpTstpwgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmRhdV9pbnN0YWxsX3BheW1lbnQkbG9nX21vbnRoIDwtIHN1YnN0cihkYXVfaW5zdGFsbF9wYXltZW50JGxvZ19kYXRlLCAxLCA3ICkNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgSW5zdGFsbF9tb250aCDsm5Qg7LaU7LacIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCmRhdV9pbnN0YWxsX3BheW1lbnQkaW5zdGFsbF9tb250aCA8LSBzdWJzdHIoZGF1X2luc3RhbGxfcGF5bWVudCRpbnN0YWxsX2RhdGUsIDEsIDcgKQ0KDQoNCmhlYWQoZGF1X2luc3RhbGxfcGF5bWVudCkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICDsm5Trs4Qg7LaU7LacIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KDQpkYXVfaW5zdGFsbF9wYXltZW50ICU+JSAgDQogIGdyb3VwX2J5KGxvZ19tb250aCwgdXNlcl9pZCwgaW5zdGFsbF9tb250aCkgJT4lICANCiAgc3VtbWFyaXNlKHBheW1lbnQgPSBzdW0ocGF5bWVudCkpIC0+IG1hdS5wYXltZW50DQoNCg0KYGBgDQoNCg0KNS4g7Iug6recL+ycoOyggOulvCDqtazrtoTtlZjripQg7ZWt66qpIOy2lOqwgO2VmOq4sCANCg0K7ISk7LmY7ZWcIOuLrOqzvCDrk6TslrTsmKgg64us7J20IOqwmeycvOuptCA9IOyLoOq3nChpbnN0YWxsKSwg7JWE64uI66m0IOq4sOyhtCDqs6DqsJ0gKGV4aXN0aW5nKQ0KDQpgYGB7cn0NCg0KaWZlbHNlKG1hdS5wYXltZW50JGluc3RhbGxfbW9udGggPT0gbWF1LnBheW1lbnQkbG9nX21vbnRoLCANCiAgICAgICAiaW5zdGFsbCIsICJleGlzdGluZyIpIC0+IG1hdS5wYXltZW50JHVzZV90eXBlDQoNCg0KaGVhZChtYXUucGF5bWVudCkNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgbG9nX2RhdGUgKOq1rOunpO2VnCDrgqDsp5wpIOyZgCDquLDsobQv7Iug6recIOqzoOqwnSDqtazrtoTsnYQg6riw7KSA7Jy866GcIOyghOyytCDsp4Drtogg6riI7JWh7J2EIOyCtO2OtOuzuOuLpC4gIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQptYXUucGF5bWVudCAlPiUgIA0KICBncm91cF9ieShsb2dfbW9udGgsIHVzZV90eXBlKSAlPiUgIA0KICBzdW1tYXJpc2UodG90YWwucGF5bWVudCA9IHN1bShwYXltZW50KSkgLT4gcGF5bWVudF9zdW1tYXJ5IA0KYGBgDQoNCjYuIOq3uOuemO2UhCDsi5zqsIHtmZQgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHNjYWxlcykNCg0KZ2dwbG90KHBheW1lbnRfc3VtbWFyeSwgYWVzKHg9IGxvZ19tb250aCwgeT10b3RhbC5wYXltZW50LCBmaWxsPSB1c2VfdHlwZSkpKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9Y29tbWEpDQoNCg0KYGBgDQoNCg0KYGBge3J9DQoNCnN1bW1hcnkobWF1LnBheW1lbnQkcGF5bWVudCkNCg0KbWF1LnBheW1lbnQgJT4lIA0KICBmaWx0ZXIodXNlX3R5cGUgPT0gImluc3RhbGwiICYgDQogICAgICAgICAgcGF5bWVudCA+IDApICU+JSAgICAgICMg7Iug6rec7J24642wIOuPiOydhCDrgrgg7IKs656M66eMIOy2lOy2nCANCiAgZ2dwbG90KGFlcyh4PXBheW1lbnQsIGZpbGw9bG9nX21vbnRoKSkrDQogIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIiwgYmlud2lkdGggPSAyMDAwMCkNCg0KDQpgYGANCg0KDQpDb25jbHVzdGlvbiANCg0KICAgIDEuIOunpOyDgeydtCDrlqjslrTsoYzri6QgKOyCrOyLpCkgDQogICAgMi4g7Iug6recIOycoOyggOydmCDrp6Tsg4HsnbQg6rCQ7IaM7ZaI6rOgLOq3uCDspJHsl5DshJwgMjAsMDAw7JuQIOydtO2VmOydmCDrnbzsnbTtirgg7Jyg7KCA7J2YIOqwkOyGjOqwgCDsu7jri6QgKOyCrOyLpCkNCiAgICAzLiDqtJHqs6Ag67mE7Jqp7J2EIOuWqOyWtO2KuOumrOuptCDslYjrkJzri6QuIA0KDQoNCiMgQ2hhcHRlciA0IDog7J207YOIIOqzoOqwnSDrtoTshJ0gKO2DiO2HtO2VmOuKlCDsnbTsnKDrpbwg67aE7ISdKSAtIO2BrOuhnOyKpCDsp5Hqs4QgDQoNCg0KMS4gVVNFUiDsnZgg7IiY6rCAIOqwkOyGjCDtlojsnYwgIA0KICANCiAg7JuQ7J247JeQIOuMgO2VnCDqsIDshKQgMSkg6rSR6rOgIOuVjOusuOyXkCDtg4jth7Qg7Jyg7KCA7IiY6rCAIOuNlCDrp47ri6QuIC0g6rSR6rOg64qUIOyngOuCnCDri6zsnbTrnpEg67mE7Iq37ZaI6rOgLCDsi6Dqt5wg7Jyg7KCA64+EIOyeiOuLpC4gDQogICAgICAgICAgICAgICAgICAgMikg66ek7JuUIO2FjOuniOulvCDrsJTqv5TshJwg6rCc7LWc7ZWY642YIOydtOuypO2KuOqwgCDsi53sg4HtlbTsoYzri6QuIC0g7J2067Kk7Yq4IOuCtOyaqeuPhCDqsbDsnZgg67CU64CM7KeAIOyViuyVmOuLpC4gDQogICAgICAgICAgICAgICAgICAgMykg7Yq57KCVIOqzoOqwney4teydmCDsnbTtg4jsnbQg7J6I64ukLiAtIOycoOugpSDqsIDshKQgDQogICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgDQogIOybkOyduOydhCDsoJzrjIDroZwg7JWMIOyImCDsl4bquLAg65WM66y47JeQICftg5Dsg4ntmJUnIOuNsOydtO2EsCDrtoTshJ3snYQg7Iuk7Iuc7ZaJIA0KICAgDQogICog7YGs66Gc7IqkIOynkeqzhOulvCDsgqzsmqntlZjsl6wg7IS46re466i87Yq4IOuPhOy2nCDrtoTshJ0gDQogICAgICAgICAgICAgICAgICAgDQoyLiDrjbDsnbTthLAg7ISg7KCVIA0KDQogICBEQVUgLSAxIO2ajCDsnbTsg4Eg67Cp66y4IOuNsOydtO2EsCANCiAgIA0KICAgICAgICAgLSBsb2dfZGF0ZSwgYXBwX25hbWUsIGlkIA0KICAgDQogICBVU0VSLklORk8gLSDsgqzsmqnsnpAg6rOg6rCdIOygleuztCDqtIDroKgg642w7J207YSwIA0KICAgDQogICAgICAgICAtIGluc3RhbGxfZGF0ZSwgYXBwX25hbWUsIGlkLCBnZW5kZXIsIGdlbmVyYXRpb24sIGRldmljZV90eXBlIA0KICAgDQoNCmBgYHtyfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICDrjbDsnbTthLAg67aI65+s7Jik6riwIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpzZXR3ZCgnQzovVXNlcnMvQWRtaW5pc3RyYXRvci9EZXNrdG9wL1IgQW5hbHlzaXMvQnVzaW5lc3MgUiBBYW5seXNpcyBzb3VyY2Uvc291cmNlL0RhdGFBbmFseXNpc19zcmMvUicpDQoNCnJlYWQuY3N2KCJzZWN0aW9uNC1kYXUuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IFQpIC0+IGRhdV80DQpyZWFkLmNzdigic2VjdGlvbjQtdXNlcl9pbmZvLmNzdiIsIGhlYWRlciA9IFQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBUKSAtPiB1c2VyX2luZm8NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgREFVICsgVVNFUl9JTkZPIA0KIyAgaWQg7JmAIGFwcF9uYW1lIOq4sOykgOycvOuhnCDrs5HtlakgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KbWVyZ2UoZGF1XzQsIHVzZXJfaW5mbywgYnkgPSBjKCJ1c2VyX2lkIiwgImFwcF9uYW1lIikpIC0+IGRhdV91c2VyX2luZm8NCg0KDQojaGVhZChkYXVfdXNlcl9pbmZvKQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIOyblOuzhOuhnCDrs7TquLAg7JyE7ZW07IScIHN1YnN0ciDstpTstpwgIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpzdWJzdHIoZGF1X3VzZXJfaW5mbyRsb2dfZGF0ZSwgMSwgNykgLT4gZGF1X3VzZXJfaW5mbyRsb2dfbW9udGgNCg0KDQpgYGANCg0KDQozLiBHZW5kZXIsIEdlbmVyYXRpb24sIERldmljZV90eXBlIOuzhCDsp5Hqs4TtlZjquLAgDQoNCjMuMSBHZW5kZXIgDQoNCiAgIEdlbmRlciDrs4Qg67Cp66y47JeQIOuMgO2VnCDsp5Hqs4Qg7J206riwIOuVjOusuOyXkCwgbG9nX21vbnRoIOulvCDsp5Hqs4TtlZzri6QuIA0KICAgDQpgYGB7cn0NCiMgZHBsciDsgqzsmqkgDQpkYXVfdXNlcl9pbmZvICU+JSANCiAgZ3JvdXBfYnkobG9nX21vbnRoKSAlPiUgIA0KICBjb3VudChnZW5kZXIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KI3RhYmxlIO2VqOyImCDsgqzsmqkgDQoNCnRhYmxlKGRhdV91c2VyX2luZm9bICwgYygibG9nX21vbnRoIiwgImdlbmRlciIpXSkNCg0KYGBgDQoNCnRhYmxlIO2VqOyImOuhnCBDcm9zcyDqtZDssKgg7KeR6rOEIC0g7Yq57KeVOiB0YWJsZSDtlajsiJjqsIAg65GQIGZhY3RvciDsnZgg7KeR6rOE66W8IOuztOq4sOyXlCDsoovri6QuIA0KDQrshLHrs4Tsl5Ag65Sw66W4IOuwqeusuCDsnKDsoIDsnZgg7IiY6rCAIOykgOqygyDqsJnsp4Ag7JWK64ukLiANCg0KMy4yIEdlbmVyYXRpb24gDQoNCmBgYHtyfQ0KDQp0YWJsZShkYXVfdXNlcl9pbmZvWywgYygibG9nX21vbnRoIiwgImdlbmVyYXRpb24iKV0pDQpgYGANCg0K7Jew66C5IOuzhCDssKjsnbTrj4Qg7JeG64qUIOqygyDqsJnslYQg67O07J2464ukLiANCg0KMy4zIEdlbmRlciB4IEdlbmVyYXRpb24gDQoNCmRjYXN0IO2VqOyImCDsgqzsmqkgOiDrkZAgZmFjdG9yIO2YlSDtgazroZzsiqQg7KeR6rOEIOyCrOyaqSANCg0KYGBge3J9DQpsaWJyYXJ5KHJlc2hhcGUyKQ0KDQoNCmRjYXN0KGRhdV91c2VyX2luZm8sIGxvZ19tb250aCB+IGdlbmRlciArIGdlbmVyYXRpb24sICMgbG9nX21vbnRoIOuzhCBnZW5kZXIg7JmAIGdlbmVyYXRpb24g7J2YIOyhsO2VqeydhCDrjZTtlbTshJwgDQogICAgICB2YWx1ZS52YXIgPSAidXNlcl9pZCIpICN1c2VyX2lkIOulvCDshLjrnbwgDQoNCmBgYA0KDQrshLHrs4QgKyDrgpjsnbTsl5DshJzrj4Qg65qc66C37ZWY6rKMIOydtO2DiOydtCDsnbzslrTrgpwg6rKDIOqwmeyngCDslYrslYQg67O07J2464ukLiANCg0KMy40IERldmljZSANCg0KDQpgYGB7cn0NCg0KdGFibGUoZGF1X3VzZXJfaW5mb1sgLGMoImxvZ19tb250aCIsICJkZXZpY2VfdHlwZSIpXSkNCg0KYGBgDQoNCg0K7LCo7J206rCAIOuCmO2DgOuCqCANCg0KYGBge3J9DQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyDrgqDsp5zsmYAg64uo66eQ6riwIOuzhCB1c2VyX2lkIOyEuOyWtOuztOq4sCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZGF1X3VzZXJfaW5mbyAlPiUgDQogIGdyb3VwX2J5KGxvZ19kYXRlLCBkZXZpY2VfdHlwZSkgJT4lICANCiAgc3VtbWFyaXNlKG4gPW4oKSkgLT4gZGV2aWNlLnN1bW1hcnkNCg0KZGV2aWNlLnN1bW1hcnkkbG9nX2RhdGUgPC0gYXMuRGF0ZShkZXZpY2Uuc3VtbWFyeSRsb2dfZGF0ZSkNCg0KDQoNCmdncGxvdChkZXZpY2Uuc3VtbWFyeSwgYWVzKHg9IGxvZ19kYXRlLCB5ID0gbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9ZGV2aWNlX3R5cGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXA9ZGV2aWNlX3R5cGUpKSsNCiAgZ2VvbV9wb2ludChzaXplPTMpKw0KICBnZW9tX2xpbmUoc2l6ZT0wLjUpKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikNCiAgDQoNCmBgYA0KDQoNCiMgQS9CIFRlc3QgDQoNCkEvQiBUZXN0IOuegCwg7Ju5IOyCrOydtO2KuCDrsKnrrLjsnpDrpbwg7J6E7J2Y7J2YIOuRkCDsp5Hri6jsnLzroZwg64KY64iE6rOgLCBBIOynkeuLqOyXkOqyjOuKlCDquLDsobQg7IKs7J207Yq466W8LCBCIOynkeuLqOyXkOqyjOuKlCDsg4jroZzsmrQg7IKs7J207Yq466W8IOuztOyXrOykgCDri6TsnYwsIOuRkCDsp5Hri6gg7KSRIOyWtOuWpCDsp5Hri6jsnbQg642UIOuGkuydgCDshLHqs7zrpbwg67O07Jes7KO864qU7KeA7JeQIOuMgO2VtCDtj4nqsIDtlZjripQg67Cp7Iud7J2064ukLiANCg0K7ISx6rO8IOq4sOykgOydgCA6IO2ajOybkCDqsIDsnoXrpaAsIOyerOuwqeusuOycqCwg6rWs66ek7KCE7ZmY66WgIOuTseydmCDsp4DtkZzrpbwg7ZmV7J247ZWc64ukLiANCg0KDQrsnbjqs7wg6rSA6rOE66W8IOywvuq4sCDsnITtlZwg7YWM7Iqk7Yq4IA0KICAgDQogICAg7KO87J2YIO2VoCDsoJAgOiDsnoTsnZjsoIEg7ZWg64u5IChSYW5kb20gQXNzaWdubWVudCkNCiAgICANCiAgICAg6re466O5IEEvQuulvCDrgqgv7JesIO2YueydgCDsp53siJgv7ZmA7IiYLCDssqsg7J287KO87J28IOuwqeusuCDsnpAv6re4IOuLpOydjCDsnbzso7zsnbwg67Cp66y47J6QICAgICAg65OxIOyehOydmOyggeydtOyngCDslYrsnYAg67Cp7Iud7Jy866GcIOq1rOu2hCDtlZjripQg6rK97JqwLCDrkZAg7KeR64uo7J2YIOywqOydtOqwgCDrrLTsl4cgICAgICAgICAgICDrlYzrrLjsnbjsp4Ag6rCA66Ck64K8IOyImOqwgCDsl4bri6QuIA0KICAgICANCiAgICAg7J6E7J2Y7KCBIOy2lOy2nCDsmIjsi5wpIOy0iOuTse2Vmeq1kCDtlZnsg53rk6TsnYQg64yA7IOB7Jy866GcIO2VnCDsi6Ttl5jsnZgg6rKw6rO866W8IOy0iOykkeqzoCAgICAgICAgIO2VmeyDneyXkOqyjCDrqqjrkZAg7KCB7Jqp7ZWY6rGw64KYLCDtjpjsnbTsiqQg67aBIOyCrOyaqeyekOulvCDrjIDsg4HsnZgg7Iuk7ZeY7J2EIO2KuOychO2EsOyXkCAgICAgICAgICDsoIHsmqnsi5ztgqTqsbDrgpgg7ZWY66m0IOynkeuLqOydmCDshLHqsqnqs7wg66qo7KeR64uo7J2YIOyEseqyqeyXkCDssKjsnbTqsIAg7J6I6riwIOuVjOusuOyXkCAgICAgICAgICAg6riw64yA7JmAIOuLpOuluCDqsrDqs7zrpbwg64KY7YOA64K8IOyImCDsnojri6QuIA0KICAgICANCiAgICAgDQogICAgIDEpIOyghO2bhCDruYTqtZAgWCANCiAgICAgICBCZWZvcmUvIEFmdGVyIOuhnCDtmZXsnbjtlZjrqbQsIOyZuOu2gCDsmpTsnbjsnYQg67Cw7KCcIO2VoCDsiJgg7JeG64ukLiANCiAgICAgICANCiAgICAgICDsmIjrpbwg65Ok7Ja0LCDrsLDrhIggQuulvCDqtJHqs6DtlZjripQg642wIOyghOyytCDqtazrp6TsnKjsnbQg7KKL7JWY64ukLiANCiAgICAgICAgICAgICAgICAgIOuwsOuEiCBC66W8IOq0keqzoO2VmOuKlOuNsCDri6Trpbgg7J2067Kk7Yq46rCAIOuMgOuwleydhCDss6Tri6QuIA0KICAgICAgICAgICAgICAgICAg67Cw64SIIELrpbwg6rSR6rOg7ZWY64qU642wIOuwqeyGoeyXkCDshozqsJzrkJjsl4jri6QuIA0KICAgICAgICAgICAgICAgICAgDQogICAgICAgPj4g7Jm467aA7JqU7J247J20IOyekeyaqe2VmOyYgOq4sCDrlYzrrLjsl5AsIOq3uOugh+uLpOuptCBB66W8IOuCtOqxuOyXiOyWtOuPhCDsoovslZjqsqDrhKQgDQogICAgICAgICAg6re46rG0IOyVhOustOuPhCDrqqjrpoQgDQoNCiAgICAgMikg7Ya16rOE7KCBIOqwgOyEpCBULlRFU1Qg64qUIOy7pO2KuOudvOyduOycvOuhnOunjCDrsJTrnbzrs7TquLAgDQogICAgIA0KICAgICDsoIHslrTrj4Qg6rCA7ISkIOqygOygleyXkOyEnOuKlCDsnZjrr7jqsIAg7J6I64qUIOywqOydtOqwgCDrgpjtg4DrgqzsnLzrr4DroZwg67mE7KeA64uI7IqkIOyDgeyXkOyEnA0KICAgICDsnZjrr7jqsIAg7J6I64qUIOywqOydtOyduOyngCDqsoDthqDrpbwg7ZW067SQ7JW87ZWc64ukLiANCg0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAg642w7J207YSwIOu2iOufrOyYpOq4sCANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0Kc2V0d2QoJ0M6L1VzZXJzL0FkbWluaXN0cmF0b3IvRGVza3RvcC9SIEFuYWx5c2lzL0J1c2luZXNzIFIgQWFubHlzaXMgc291cmNlL3NvdXJjZS9EYXRhQW5hbHlzaXNfc3JjL1InKQ0KDQpyZWFkLmNzdigic2VjdGlvbjUtYWJfdGVzdF9pbXAuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IFQpIC0+IHRlc3RfaW1wDQpyZWFkLmNzdigic2VjdGlvbjUtYWJfdGVzdF9nb2FsLmNzdiIsIGhlYWRlciA9IFQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBUKSAtPiANCiAgdGVzdF9nb2FsDQoNCg0KYGBgDQoNCg0K642w7J207YSwIOyEpOuqhSA8IOuwsOuEiCDqtJHqs6DsnZgg7ZGc7IucIO2ajeyImCDsoJXrs7Q+DQogICBsb2dfZGF0ZSA6IO2RnOyLnCDrgqDsp5wgDQogICB0ZXN0X25hbWUgOiB0ZXN0IOydtOumhCANCiAgIHRlc3RfY2FzZSA6IEEsIEIgDQogICB1c2VyX2lkIA0KICAgdHJhbnNjYXRpb25faWQgOiDrsLDrhIgg6rSR6rOg6rCAIO2RnOyLnOuQmOyXiOydhCDrlYwg67Cc7IOd7ZWY64qUIGlkDQoNCg0KYGBge3J9DQoNCkRUOjpkYXRhdGFibGUodGVzdF9pbXApDQpgYGANCg0K642w7J207YSwIOyEpOuqhSA867Cw64SIIOq0keqzoOydmCDtgbTrpr8g7Zqf7IiYIOygleuztD4gDQogICBsb2dfZGF0ZSA6IO2BtOumre2VnCDrgqDsp5wgDQogICB0ZXN0X25hbWUgOiB0ZXN0IOydtOumhCANCiAgIHRlc3RfY2FzZSA6IEEsIEIgDQogICB1c2VyX2lkIA0KICAgdHJhbnNjYXRpb25faWQgOiDrsLDrhIgg6rSR6rOg6rCAIO2RnOyLnOuQmOyXiOydhCDrlYwg67Cc7IOd7ZWY64qUIGlkDQoNCmBgYHtyfQ0KDQpEVDo6ZGF0YXRhYmxlKHRlc3RfZ29hbCkNCmBgYA0KDQoNClRyYW5zY2F0aW9uX2lkIOq4sOykgCA6IOuwsOuEiOq0keqzoOqwgCDtkZzsi5zrkJjsl4jsnYTrlYwg67Cc7IOd7ZWY64qUIGlkIOuhnCDtkZzsi5zroZzqt7jsmYAg7YG066atIOuhnOq3uOulvCDqsrDtlantlaAg7YKk66GcIOyCrOyaqSANCg0K7KaJLCDtkZzsi5zsmYAg7YG066atIOq4sOuhneydtCDrp57ripQg6riw7KSA7Jy866GcIOuzke2VqSANCg0KYGBge3J9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgIOuNsOydtO2EsCDtlansuZjquLAgDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KYWIudGVzdC5pbXAgPC0gbWVyZ2UodGVzdF9pbXAsIHRlc3RfZ29hbCwgYnkgPSAidHJhbnNhY3Rpb25faWQiLCANCiAgICAgICAgICAgICAgICAgICAgIGFsbC54ID0gVCwgc3VmZml4ZXMgPSBjKCIiLCAiLmciKSkNCg0KDQpoZWFkKGFiLnRlc3QuaW1wKQ0KYGBgDQoNCg0KYGBge3J9DQoNCmFiLnRlc3QuaW1wWzM2LF0NCg0KYGBgDQoNCuuwsOuEiCDqtJHqs6AgQuqwgCB1c2VyX2lkIDM1MzE1IOyXkOqyjCDtkZzsi5wg65CcIOuCoOynnOqwgCAxMOyblCAy7J28IOydtOyYgOuKlOuNsCwg7ZW064u5IOycoOyggOuKlCBC66W8IO2BtOumre2WiOuLpC4gDQrsnbQgdHJhbnNjYXRpb25faWQg64qUIDM2IOuyiOydtOuLpC4gDQoNCg0K7YG066atIOycoOustCDtlIzrnpjqt7gg7J6R7ISx7ZWY6riwIA0KDQpVc2VyX2lkLmcg64qUIO2BtOumreydhCDtlZwg7JWE7J2065SU66W8IOuCmO2DgOuCtOyjvOq4sCDrlYzrrLjsl5AgTkEg7J2066m0IO2BtOumreydhCDslYgg7ZaI64uk64qUIOqygyANCg0KYGBge3J9DQoNCmlmZWxzZShpcy5uYShhYi50ZXN0LmltcCR1c2VyX2lkLmcpLCAwICwgMSkgLT4gYWIudGVzdC5pbXAkaXNfZ29hbCANCg0KYGBgDQoNCg0K7YG066at66WgIOynkeqzhO2VmOq4sCANCg0KYGBge3J9DQoNCmxpYnJhcnkoZHBseXIpDQoNCmFiLnRlc3QuaW1wICU+JSAgDQogIGdyb3VwX2J5KHRlc3RfY2FzZSkgJT4lIA0KICBzdW1tYXJpc2UoY3ZyPXN1bShpc19nb2FsKS8gbGVuZ3RoKHVzZXJfaWQpKSAgIyMg7YG066at7ZWcIOyCrOuejOydmCDtlakgLyDrsLDrhIgg6rSR6rOg7JeQIO2RnOyLnOuQnCDsgqzrnowg7IiYIA0KDQoNCmBgYA0KDQoNCngyIOyLpO2Wie2VmOq4sCANCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICDsubTsnbQg6rKA7KCVIDogQS9CIChUZXN0IGNhc3QpIOyXkCDrjIDtlZwg7YG066at66WgICgwLDEpIOqygOyglSANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KY2hpc3EudGVzdChhYi50ZXN0LmltcCR0ZXN0X2Nhc2UsIGFiLnRlc3QuaW1wJGlzX2dvYWwpDQoNCmBgYA0KDQoNCu2FjOyKpO2KuCDsvIDsnbTsiqTrs4Qg7YG066at7JyoIOyCsOy2nO2VmOq4sCANCg0KMSkg64Kg7KecLCDthYzsiqTtirgg7LyA7J207IqkIOuzhCDsgrDstpwgDQoNCmBgYHtyfQ0KDQogc3VtbWFyeS5pbXAgPC0gYWIudGVzdC5pbXAgJT4lICANCiAgZ3JvdXBfYnkobG9nX2RhdGUsIHRlc3RfY2FzZSkgJT4lIA0KICBzdW1tYXJpc2UoIGltcD0gbGVuZ3RoKHVzZXJfaWQpLCAgICMgdXNlcl9pZCDsubTsmrTtirggDQogICAgICAgICAgICBjdj0gc3VtKGlzX2dvYWwpLCAgICAjIGlzX2dvYWzsnZgg7ZWpIA0KICAgICAgICAgICAgY3ZyID0gcm91bmQoc3VtKGlzX2dvYWwpL2xlbmd0aCh1c2VyX2lkKSwzKSwgI+uRkCDsiJjsuZjrpbwg64KY64iIIOu5hOycqCANCiAgICAgICAgICAgIGN2ci5hdmcgPSByb3VuZChzdW0oY3YpL3N1bShpbXApLDMpKSAgDQoNCg0KRFQ6OmRhdGF0YWJsZShzdW1tYXJ5LmltcCkNCmBgYA0KDQoNCjIpIOyLnOqwge2ZlCANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCnN1bW1hcnkuaW1wJGxvZ19kYXRlIDwtYXMuRGF0ZShzdW1tYXJ5LmltcCRsb2dfZGF0ZSkNCg0KICBnZ3Bsb3Qoc3VtbWFyeS5pbXAsIGFlcyh4PWxvZ19kYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeT1jdnIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHRlc3RfY2FzZSkpKw0KICAgIGdlb21fbGluZSgpKw0KICAgIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0KDQoNCiMgQ2hhcHRlciA1IDogQS9CIFRlc3QgLTIgDQoNCg0KQS5CIFRlc3QgOiDrsKnrrLjsnpAg7IiY6rCAIOu5hOyKt+2VnCDrkZAg6rCc7J2YIOybuSDtjpjsnbTsp4Drpbwg67mE6rWQIA0KDQrsnbzrsJjsoIHsnLzroZwg7IOI66Gt6rKMIOuPhOyehe2VmOuKlCDquLDriqXsnbQg7J2Y64+E7ZWcIOuwlOuMgOuhnCDtmqjqs7zqsIAg7J6I7J2E7KeAIO2ZleyduO2VmOq4sCDsnITtlbTshJzsnbTri6QuIA0KDQoNCkNBU0UgU1RVRFkgLSDrkZAg6rCc7J2YIO2YuO2FlCDsmIjslb0g7IKs7J207Yq4IEEvQiDthYzsiqTtirggDQoNCkRhdGEgU3VtbWFyeSANCg0KMS4gVmFyaWFuY2UgQSA6IOq4sOyhtOydmCDsoJztkogg7Zi57J2AIOq4sOuKpeydhCDrrJjsgqztlZjripQg7Ya17KCcIOynkeuLqCANCg0KMi4gVmFyaWFuY2UgQiA6IOyDiOuhnOyatCDsoJztkogg7Zi57J2AIOq4sOuKpeydhCDrrJjsgqztlZjsl6wsIOyCrOyaqeyekOqwgCDsoovslYTtlZjripTsp4Ag7JiI7JW9IOyImOqwgCDripjsl4jripTsp4Ag7ZmV7J247ZWY64qUIOyYiOyVvSDsp5Hri6ggDQoNCjMuIENvbnZlcnRlZCAtIOyghO2ZmOuloCBUL0YNCg0KDQo0LiDqsIDshKQg7ISk7KCVIA0KDQogIEhvOiBBICjquLDsobQpLCBCKOyLoOq3nCkg7KeR64uoIOyCrOydtOydmCDsoITtmZjrpaDsnYAg6rCZ64ukLiANCiAgICAgICg9IO2aqOqzvOqwgCDsl4bri6QpDQogICAgICANCiAgSDE6IO2aqOqzvOqwgCDsnojri6QuIA0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGV4dHJhZm9udCkNCg0KDQoNCmxvYWQoJ0M6L1VzZXJzL0FkbWluaXN0cmF0b3IvRGVza3RvcC9SIEFuYWx5c2lzL0J1c2luZXNzIFIgQWFubHlzaXMgc291cmNlL3NvdXJjZS9EYXRhQW5hbHlzaXNfc3JjL1IvQUJUZXN0LnJkYScpIA0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBBIOq3uOujuSDsg53shLEgOiBWYXJpYW50ID0gQSDsmYAgY29udmVydGVkID0gVFJVRSANCiMgIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KQUJUZXN0ICU+JSAgDQogIGZpbHRlcih2YXJpYW50ID09ICJBIiAmIGNvbnZlcnRlZCA9PSAiVFJVRSIpIC0+IHN1YnNldF9BDQoNCg0KRFQ6OmRhdGF0YWJsZShzdWJzZXRfQSkNCg0KDQpgYGANCg0KDQpgYGB7cn0NCiMjIENvbnZlcnRpb24g7Iir7J6QIEHqt7jro7nsnZggbnJvdyDqsJIgDQpucm93KHN1YnNldF9BKSAtPiBjb252ZXJzaW9uX0ENCg0KY29udmVyc2lvbl9BICAgIzIwIA0KDQojIyBWaXNpdG9yIOyIq+yekCA6IEFCX3Rlc3Qg7KCE7LK0IO2FjOydtOu4lOyXkOyEnOydmCBBIOqwkuunjCDstpTstpztlZwgbnJvdyDqsJIgDQoNCm5yb3coQUJUZXN0ICU+JSAgZmlsdGVyKHZhcmlhbnQgPT0gIkEiKSkgLT4gdmlzaXRvcl9BDQoNCnZpc2l0b3JfQSAjNzIxIA0KDQojIyBBIOq3uOujuSDsgqzrnowg7IiYIC8g6rSR6rOgIEEg7JeQIOuwqeusuO2VnCDsgqzrnowg7IiYIA0KDQpjb252X3JhdGVfQSA8LSAoY29udmVyc2lvbl9BL3Zpc2l0b3JfQSkNCmNvbnZfcmF0ZV9BICAjMC4yNzcNCmBgYA0KDQoNCmBgYHtyfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBBIOq3uOujuSDsg53shLEgOiBWYXJpYW50ID0gQSDsmYAgY29udmVydGVkID0gVFJVRSANCiMgIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KQUJUZXN0ICU+JSANCiAgZmlsdGVyKHZhcmlhbnQgPT0gIkIiICYgY29udmVydGVkID09ICJUUlVFIikgLT4gc3Vic2V0X0IgDQoNCg0KIyBDb252ZXJ0aW9uIOyIq+yekCANCg0KbnJvdyhzdWJzZXRfQikgLT4gY29udmVyc2lvbl9CDQoNCiMgQiDqt7jro7nsl5Ag67Cp66y47ZWcIOuwqeusuOqwnSANCg0KbnJvdyhBQlRlc3QgJT4lIGZpbHRlcih2YXJpYW50ID09ICJCIikpIC0+IHZpc2l0b3JfQg0KDQoNCiMg7KCE7ZmY66WgIA0KDQpjb252X3JhdGVfQiA8LSAoY29udmVyc2lvbl9CL3Zpc2l0b3JfQikNCmNvbnZfcmF0ZV9CDQoNCg0KDQojIFVwbGlmdCA6IEIg67mE7JyoIC0gQSDsnbTsnKggLyBBIOu5hOycqCAqIDEwMCANCg0KKGNvbnZfcmF0ZV9CIC0gY29udl9yYXRlX0EpLyBjb252X3JhdGVfQSAqMTAwICM4Mi43OQ0KYGBgDQoNCkIg6rCAIEEg67O064ukIDgyJSDrjZQg64aS64ukLiANCg0KKipQb29sZWQgUHJvYmFiaWxpdHkgZm9yIFRlc3QgVmVyc2lvbnMgQSAmIEIqKg0KDQpgYGB7cn0NCg0KI1Bvb2xlZCBQcm9iYWJpbGl0eSDqs7Xsi506IA0KIyAoQSDsnZgg7KCE7ZmY66WgICsgQuydmCDsoITtmZjrpaApIC8gKEEg67Cp66y46rCdICsgQiDrsKnrrLjqsJ0pDQoNCihjb252X3JhdGVfQSArIGNvbnZfcmF0ZV9CKSAvICh2aXNpdG9yX0EgKyB2aXNpdG9yX0IpIC0+IHBfcG9vbA0KcF9wb29sDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQo=