.columns{display:flex;}
h1{color:red}
h2{color:blue}
h3{color:green}
Phastar Presentation

Phastar Presentation

Why Clinical trial Should Embrace the R language

Why Clinical trial Should Embrace the R language

Why is data manipulation important?

-Data manipulation is the changing of data to make it easier to read or be more organized.

-Companies may manipulate their data because it can provide them with well-organized databases. Categorization can allow companies to group similar data, which may make it easier to search for information.

-Another reason companies might manipulate their data is that it can allow them to archive project data and access it later if they want to use it as a reference while working on a new project or setting business goals.

-Data manipulation is also a valuable tool in identifying and correcting data redundancies in reporting.

Data cleaning functions

  1. The filter() function subsets the rows in a data frame by testing against a conditional statement.

  2. Whereas filter() subsets a dataframe by row, select() returns a subset of the columns.

  3. mutate() adds new columns of data.

  4. arrange() is the simplest of the dplyr functions, which orders rows according to values in a given column. The default is to order numbers from lowest -> highest.

  5. The summarize(), which as the name suggests, creates individual summary statistics from larger data sets.

  6. group_by() allows us to create sub-groups based on labels in a particular column, and to run subsequent functions on all sub-groups.

  7. (%>%) The point of the pipe is to help you write code in a way that is easier to read and understand

1. DM Domain- data manipulation

Checking data and reading columns names

library(xaringanthemer)
suppressPackageStartupMessages(library(haven))
dmdata=haven::read_sas("dm.sas7bdat")
suppressPackageStartupMessages(library(tidyverse))#package for data manipulation
names(dmdata)  #checking the variables in the data
##  [1] "STUDYID"  "DOMAIN"   "USUBJID"  "SUBJID"   "RFSTDTC"  "RFENDTC" 
##  [7] "RFXSTDTC" "RFXENDTC" "RFICDTC"  "RFPENDTC" "DTHDTC"   "DTHFL"   
## [13] "SITEID"   "BRTHDTC"  "AGE"      "AGEU"     "SEX"      "RACE"    
## [19] "ETHNIC"   "ARM"      "ACTARM"   "COUNTRY"  "ARMCD"    "ACTARMCD"

Checking data structure

glimpse(dmdata) #checking the if the variable is numeric ,character etc
## Rows: 21
## Columns: 24
## $ STUDYID  <chr> "AB-5365-101", "AB-5365-101", "AB-5365-101", "AB-5365-101", "…
## $ DOMAIN   <chr> "DM", "DM", "DM", "DM", "DM", "DM", "DM", "DM", "DM", "DM", "…
## $ USUBJID  <chr> "AB-5365-101-001-1001", "AB-5365-101-001-4002", "AB-5365-101-…
## $ SUBJID   <chr> "001-1001", "001-4002", "001-4004", "002-1001", "002-3003", "…
## $ RFSTDTC  <chr> "2018-04-03T11:24", "2018-04-12T20:40", "2019-03-04T23:30", "…
## $ RFENDTC  <chr> "2018-07-23", "2019-05-06", "2019-04-01", "2018-11-03", "2019…
## $ RFXSTDTC <chr> "2018-04-03T11:24", "2018-04-12T20:40", "2019-03-04T23:30", "…
## $ RFXENDTC <chr> "2018-04-23", "2019-04-28", "2019-03-17", "2018-06-20", "2019…
## $ RFICDTC  <chr> "2018-03-12", "2019-02-01", "2019-02-20", "2018-04-24", "2019…
## $ RFPENDTC <chr> "2018-07-23", "", "2019-04-01", "2018-11-03", "2019-04-22", "…
## $ DTHDTC   <chr> "2018-07-23", "", "2019-04-01", "2018-11-03", "", "", "", "",…
## $ DTHFL    <chr> "Y", "", "Y", "Y", "", "", "", "", "", "", "", "", "", "Y", "…
## $ SITEID   <chr> "001", "001", "001", "002", "002", "002", "012", "012", "012"…
## $ BRTHDTC  <chr> "1956-07-15", "1940-05-23", "1937-01-22", "1934-03-16", "1935…
## $ AGE      <dbl> 61, 78, 82, 84, 83, 66, 58, 61, 65, 66, 76, 87, 68, 69, 63, 8…
## $ AGEU     <chr> "YEARS", "YEARS", "YEARS", "YEARS", "YEARS", "YEARS", "YEARS"…
## $ SEX      <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "F", "M", "M", "…
## $ RACE     <chr> "WHITE", "WHITE", "WHITE", "WHITE", "WHITE", "WHITE", "WHITE"…
## $ ETHNIC   <chr> "NOT HISPANIC OR LATINO", "HISPANIC OR LATINO", "NOT HISPANIC…
## $ ARM      <chr> "AB-5365-101 50 MG", "AB-5365-101 100 MG", "AB-5365-101 100 M…
## $ ACTARM   <chr> "AB-5365-101 50 MG", "AB-5365-101 100 MG", "AB-5365-101 100 M…
## $ COUNTRY  <chr> "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA"…
## $ ARMCD    <chr> "AB-5365101-50", "AB-5365101-100", "AB-5365101-100", "AB-5365…
## $ ACTARMCD <chr> "AB-5365101-50", "AB-5365101-100", "AB-5365101-100", "AB-5365…

Selecting numeric columns

dmdata %>%
  select_if(is.numeric) %>%
  names()
## [1] "AGE"

Selecting character columns

dmdata %>%
  select_if(is.character) %>%
  names()
##  [1] "STUDYID"  "DOMAIN"   "USUBJID"  "SUBJID"   "RFSTDTC"  "RFENDTC" 
##  [7] "RFXSTDTC" "RFXENDTC" "RFICDTC"  "RFPENDTC" "DTHDTC"   "DTHFL"   
## [13] "SITEID"   "BRTHDTC"  "AGEU"     "SEX"      "RACE"     "ETHNIC"  
## [19] "ARM"      "ACTARM"   "COUNTRY"  "ARMCD"    "ACTARMCD"

Selection of columns by logical statement

This allow you to select a column by a given condition. If the condition is true or false.

dmdata %>%
  select_if(is.numeric) %>%
  select_if(~mean(., na.rm=TRUE) > 5) %>% head(2) %>% knitr::kable()
AGE
61
78
#shorter way
dmdata %>%
  select_if(~is.numeric(.) & mean(., na.rm=TRUE) > 5) %>%  head(2) %>% knitr::kable()#where mean is more than 5 and only numeric variables
AGE
61
78

Selecting columns based on partial column names

If you have a lot of columns with a similar structure you can use partial matching by adding starts_with(), ends_with() or contains() in your select statement.

dmdata %>%
  select( starts_with("ACT")) %>%#select columns that start with ACT
  names() %>% knitr::kable()
x
ACTARM
ACTARMCD
#ends
dmdata %>%
  select(ends_with("DTC")) %>%#select columns ending with DTC
  colnames() %>% knitr::kable()
x
RFSTDTC
RFENDTC
RFXSTDTC
RFXENDTC
RFICDTC
RFPENDTC
DTHDTC
BRTHDTC
#contains
dmdata %>%
  select(contains("RFX"))%>%#select columns that has RFX
  colnames() 
## [1] "RFXSTDTC" "RFXENDTC"

Selecting columns based on regex expression

dmdata %>%
  select(matches("RFX|ARM")) %>%
  glimpse
## Rows: 21
## Columns: 6
## $ RFXSTDTC <chr> "2018-04-03T11:24", "2018-04-12T20:40", "2019-03-04T23:30", "…
## $ RFXENDTC <chr> "2018-04-23", "2019-04-28", "2019-03-17", "2018-06-20", "2019…
## $ ARM      <chr> "AB-5365-101 50 MG", "AB-5365-101 100 MG", "AB-5365-101 100 M…
## $ ACTARM   <chr> "AB-5365-101 50 MG", "AB-5365-101 100 MG", "AB-5365-101 100 M…
## $ ARMCD    <chr> "AB-5365101-50", "AB-5365101-100", "AB-5365101-100", "AB-5365…
## $ ACTARMCD <chr> "AB-5365101-50", "AB-5365101-100", "AB-5365101-100", "AB-5365…

Selecting columns based pre-identified columns

classification <- c("STUDYID", "DOMAIN", "USUBJID", "SUBJID", "RFSTDTC", "RFENDTC", 
"RFXSTDTC", "RFXENDTC", "RFICDTC", "RFPENDTC", "DTHDTC", "DTHFL", 
"SITEID", "BRTHDTC")

dmdata %>%
  select(!!classification) %>% names()
##  [1] "STUDYID"  "DOMAIN"   "USUBJID"  "SUBJID"   "RFSTDTC"  "RFENDTC" 
##  [7] "RFXSTDTC" "RFXENDTC" "RFICDTC"  "RFPENDTC" "DTHDTC"   "DTHFL"   
## [13] "SITEID"   "BRTHDTC"

Selecting columns based on a helper functions last_col

dmdata %>% select(last_col(0:4))
## # A tibble: 21 × 5
##    ACTARMCD       ARMCD          COUNTRY ACTARM             ARM               
##    <chr>          <chr>          <chr>   <chr>              <chr>             
##  1 AB-5365101-50  AB-5365101-50  USA     AB-5365-101 50 MG  AB-5365-101 50 MG 
##  2 AB-5365101-100 AB-5365101-100 USA     AB-5365-101 100 MG AB-5365-101 100 MG
##  3 AB-5365101-100 AB-5365101-100 USA     AB-5365-101 100 MG AB-5365-101 100 MG
##  4 AB-5365101-50  AB-5365101-50  USA     AB-5365-101 50 MG  AB-5365-101 50 MG 
##  5 AB-5365101-50  AB-5365101-50  USA     AB-5365-101 50 MG  AB-5365-101 50 MG 
##  6 AB-5365101-100 AB-5365101-100 USA     AB-5365-101 100 MG AB-5365-101 100 MG
##  7 AB-5365101-50  AB-5365101-50  USA     AB-5365-101 50 MG  AB-5365-101 50 MG 
##  8 AB-5365101-50  AB-5365101-50  USA     AB-5365-101 50 MG  AB-5365-101 50 MG 
##  9 AB-5365101-100 AB-5365101-100 USA     AB-5365-101 100 MG AB-5365-101 100 MG
## 10 AB-5365101-250 AB-5365101-250 USA     AB-5365-101 250 MG AB-5365-101 250 MG
## # … with 11 more rows

Moving and rearranging columns

(dmdata %>% select(last_col(0),everything())) %>% head(2)#putting the last columns to be the first
## # A tibble: 2 × 24
##   ACTARMCD       STUDYID DOMAIN USUBJID SUBJID RFSTDTC RFENDTC RFXSTDTC RFXENDTC
##   <chr>          <chr>   <chr>  <chr>   <chr>  <chr>   <chr>   <chr>    <chr>   
## 1 AB-5365101-50  AB-536… DM     AB-536… 001-1… 2018-0… 2018-0… 2018-04… 2018-04…
## 2 AB-5365101-100 AB-536… DM     AB-536… 001-4… 2018-0… 2019-0… 2018-04… 2019-04…
## # … with 15 more variables: RFICDTC <chr>, RFPENDTC <chr>, DTHDTC <chr>,
## #   DTHFL <chr>, SITEID <chr>, BRTHDTC <chr>, AGE <dbl>, AGEU <chr>, SEX <chr>,
## #   RACE <chr>, ETHNIC <chr>, ARM <chr>, ACTARM <chr>, COUNTRY <chr>,
## #   ARMCD <chr>

Dropping empty columns

dmdata$NULLcol=NA
dmdata %>%select_if(~ any(is.na(.))) %>%names() %>% knitr::kable()#empty column
x
NULLcol
dmdata %>%select_if(~! any(is.na(.))) %>%names() %>% knitr::kable()#non-empty column
x
STUDYID
DOMAIN
USUBJID
SUBJID
RFSTDTC
RFENDTC
RFXSTDTC
RFXENDTC
RFICDTC
RFPENDTC
DTHDTC
DTHFL
SITEID
BRTHDTC
AGE
AGEU
SEX
RACE
ETHNIC
ARM
ACTARM
COUNTRY
ARMCD
ACTARMCD
suppressPackageStartupMessages(library(janitor))#calling janitor package
#(dfw=dmdata %>% remove_empty() %>% head(2)) # the fucntion drop empty columns from data
#(dfw1=dmdata %>% discard(~all(is.na(.))) %>% head(2)) 

Deselecting/deleting columns with values

We use a minus sign before the variable or the vector of the variables to be dropped

dmdata %>% 
  select(-STUDYID, -(DTHFL:ACTARMCD)) %>%
  names() 
##  [1] "DOMAIN"   "USUBJID"  "SUBJID"   "RFSTDTC"  "RFENDTC"  "RFXSTDTC"
##  [7] "RFXENDTC" "RFICDTC"  "RFPENDTC" "DTHDTC"   "NULLcol"

Dropping the missing values

1 na.omit the command drop all rows with NAs,it comes from base r

2 comlpete.case it works in similar way as na.omit

3 drop_na the function comes from ****dplyr*** package it works like na.omit

Checking null values in rows

set.seed(4321)
mt <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)#creating a matrix (10 by 10)
dt <- as.data.frame(mt)#converting the matrix to a dataframe
dt#calling the data
##    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
## 1   4  5  2  8  9  1  4  8  9  NA
## 2  10  5  5  6  8  6 10 10  3   8
## 3   9  9 10  6  2  6  6  6 10   8
## 4   4  3  6  9 NA  6 10  7  5  NA
## 5   9  6  8  4  7  3  8  3  7   2
## 6   7  9  1  2  4  2  2  3  1   6
## 7  10 10  7  3  6 NA  6  2  3   6
## 8   3  3  4 10  8  3  4 10 NA  10
## 9   3  3  5  3  4  1  4  3  1   4
## 10  4  2 NA NA  3  7  5  3  7   5

Dropping null observations

colSums(is.na(dt))
##  V1  V2  V3  V4  V5  V6  V7  V8  V9 V10 
##   0   0   1   1   1   1   0   0   1   2
dt[is.na(dt)] <- 0# assigning zero to NAs
dt# calling the new data
##    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
## 1   4  5  2  8  9  1  4  8  9   0
## 2  10  5  5  6  8  6 10 10  3   8
## 3   9  9 10  6  2  6  6  6 10   8
## 4   4  3  6  9  0  6 10  7  5   0
## 5   9  6  8  4  7  3  8  3  7   2
## 6   7  9  1  2  4  2  2  3  1   6
## 7  10 10  7  3  6  0  6  2  3   6
## 8   3  3  4 10  8  3  4 10  0  10
## 9   3  3  5  3  4  1  4  3  1   4
## 10  4  2  0  0  3  7  5  3  7   5
colSums(is.na(dt))
##  V1  V2  V3  V4  V5  V6  V7  V8  V9 V10 
##   0   0   0   0   0   0   0   0   0   0

Dealing with the null values by rows

df <- data.frame(X1 = c(1:10),
                    X2 = rep(NA, 10),
                    X3 = letters[seq( from = 1, to = 10 )],
                    X4 = rep("",10))# creating hypothetical dataset in r
df
##    X1 X2 X3 X4
## 1   1 NA  a   
## 2   2 NA  b   
## 3   3 NA  c   
## 4   4 NA  d   
## 5   5 NA  e   
## 6   6 NA  f   
## 7   7 NA  g   
## 8   8 NA  h   
## 9   9 NA  i   
## 10 10 NA  j

Dealing with the null values by rows and columns

df1=na.omit(df)#we drop all the NAs values
df1
## [1] X1 X2 X3 X4
## <0 rows> (or 0-length row.names)
df2=df[complete.cases(df),]#we keep only complete cases
df2
## [1] X1 X2 X3 X4
## <0 rows> (or 0-length row.names)

Removing NAs using rowSums fn from base r

m <- structure(c( 1,  NA,  3,  4,  5, 
                  6,  NA,  8, NA, 10, 
                 11,  NA, 13, NA, NA), 
               .Dim = c(5L, 3L))
m
##      [,1] [,2] [,3]
## [1,]    1    6   11
## [2,]   NA   NA   NA
## [3,]    3    8   13
## [4,]    4   NA   NA
## [5,]    5   10   NA
(mm=m[rowSums(is.na(m)) != ncol(m), ])#we delete all rows that have NAs
##      [,1] [,2] [,3]
## [1,]    1    6   11
## [2,]    3    8   13
## [3,]    4   NA   NA
## [4,]    5   10   NA

Deleting empty rows using apply family of functions from base r

ind <- apply(m, 1, function(x) all(is.na(x)))
m1=m[!ind,]
m1
##      [,1] [,2] [,3]
## [1,]    1    6   11
## [2,]    3    8   13
## [3,]    4   NA   NA
## [4,]    5   10   NA

Mutating/separating/uniting columns

The mutate command is used when you want to create a new column to be added to the data or to change the data type.

suppressPackageStartupMessages(library(lubridate))#we call lubridate and suppress any worning or message
dmdata %>% mutate(year1=substr(RFSTDTC,1,4),yr=year(RFSTDTC),hr=hour(RFSTDTC)) %>%
  select(year1,yr,hr) %>% head(3) %>% knitr::kable()
year1 yr hr
2018 2018 0
2018 2018 0
2019 2019 0

Mutate_if

dmdata %>%
  mutate_if(is.numeric, round,1) %>%select_if(~is.numeric(.)) %>%
  head(3) %>% knitr::kable()
AGE
61
78
82

Uniting columns

The unite function from dplyr is used to combine columns

dffd=dmdata %>%
  select(STUDYID, SUBJID) %>%unite('usubjid1',c(STUDYID,SUBJID),sep = "/",remove = F) %>% 
  head(3)
dffd %>% knitr::kable()
usubjid1 STUDYID SUBJID
AB-5365-101/001-1001 AB-5365-101 001-1001
AB-5365-101/001-4002 AB-5365-101 001-4002
AB-5365-101/001-4004 AB-5365-101 001-4004

Separating columns

The separate function from dplyr is used to combine columns

dffd %>%separate(usubjid1,c('STUDYID_a','SUBJID_b'),sep = "/",remove = F) %>% 
  head(3) %>% knitr::kable()#separating the usubjid1 by separator/
usubjid1 STUDYID_a SUBJID_b STUDYID SUBJID
AB-5365-101/001-1001 AB-5365-101 001-1001 AB-5365-101 001-1001
AB-5365-101/001-4002 AB-5365-101 001-4002 AB-5365-101 001-4002
AB-5365-101/001-4004 AB-5365-101 001-4004 AB-5365-101 001-4004

Filtering

dmdata %>% select(USUBJID,SEX,ETHNIC) %>% filter(SEX=="M" &USUBJID %in% c('AB-5365-101-017-3003','AB-5365-101-018-2004','AB-5365-101-019-2003')) %>% 
  knitr::kable() #select the identified subject and they should ne male
USUBJID SEX ETHNIC
AB-5365-101-017-3003 M NOT HISPANIC OR LATINO
AB-5365-101-019-2003 M NOT HISPANIC OR LATINO
dmdata %>% select(USUBJID,SEX,ETHNIC,AGE) %>% filter(SEX=="M" & AGE>78) %>% 
  knitr::kable()
USUBJID SEX ETHNIC AGE
AB-5365-101-001-4004 M NOT HISPANIC OR LATINO 82
AB-5365-101-002-1001 M NOT HISPANIC OR LATINO 84
AB-5365-101-002-3003 M NOT HISPANIC OR LATINO 83
AB-5365-101-012-5007 M NOT HISPANIC OR LATINO 87
AB-5365-101-017-3003 M NOT HISPANIC OR LATINO 81

Sorting/ arrange/order

dmdata %>% select(USUBJID,AGE) %>% arrange(AGE) %>% head(8)  %>%
  knitr::kable()#ascending
USUBJID AGE
AB-5365-101-012-1002 58
AB-5365-101-001-1001 61
AB-5365-101-012-3003 61
AB-5365-101-018-2004 61
AB-5365-101-017-3002 63
AB-5365-101-019-2003 64
AB-5365-101-012-4004 65
AB-5365-101-002-4004 66
dmdata %>% select(USUBJID,AGE) %>% arrange(desc(AGE)) %>% head(8)  %>% 
  knitr::kable()#descending age
USUBJID AGE
AB-5365-101-012-5007 87
AB-5365-101-002-1001 84
AB-5365-101-002-3003 83
AB-5365-101-001-4004 82
AB-5365-101-017-3003 81
AB-5365-101-001-4002 78
AB-5365-101-012-5006 76
AB-5365-101-019-1001 76

Renaming columns

The rename and select from dplyr are used in changing columns names

dmdata %>% select(SUBJID,SEX,contains('ACT')) %>% rename(Subject=SUBJID,Gender=SEX) %>% head(3)%>% 
  knitr::kable() #we use rename to change columns name
Subject Gender ACTARM ACTARMCD
001-1001 M AB-5365-101 50 MG AB-5365101-50
001-4002 M AB-5365-101 100 MG AB-5365101-100
001-4004 M AB-5365-101 100 MG AB-5365101-100
dmdata %>% select(Subject1=SUBJID,Gender1=SEX,contains('ACT')) %>%  head(3)%>% 
  knitr::kable()
Subject1 Gender1 ACTARM ACTARMCD
001-1001 M AB-5365-101 50 MG AB-5365101-50
001-4002 M AB-5365-101 100 MG AB-5365101-100
001-4004 M AB-5365-101 100 MG AB-5365101-100

Base r renaming

(names(dmdata)[4]=('SBJD'))
## [1] "SBJD"

group_by() %>% summarize()

dmdata %>%  group_by(ARM) %>%   summarise(Average = mean(AGE),sdv=sd(AGE),median_=median(AGE))%>% 
  knitr::kable()
ARM Average sdv median_
AB-5365-101 100 MG 70.14286 8.071112 66.0
AB-5365-101 250 MG 76.33333 10.503967 76.0
AB-5365-101 450 MG 68.00000 NA 68.0
AB-5365-101 50 MG 70.80000 9.863513 70.5
LS0tCnRpdGxlOiAiRGF0YSBNYW5pcHVsYXRpb24iCmF1dGhvcjogIkVyaWMgQW5qZW8iCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSlgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2M6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICB0aGVtZTogZGVmYXVsdAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsd2FybmluZyA9IEYsbWVzc2FnZSA9IEYpCmBgYAoKYGBge2Nzc30KLmNvbHVtbnN7ZGlzcGxheTpmbGV4O30KaDF7Y29sb3I6cmVkfQpoMntjb2xvcjpibHVlfQpoM3tjb2xvcjpncmVlbn0KCmBgYAoKYGBge3IgZmlndXJlbmFtZTEsIGVjaG89RkFMU0UsIGZpZy5jYXA9IlBoYXN0YXIgUHJlc2VudGF0aW9uIiwgb3V0LndpZHRoID0gJzEwMCUnfQpsaWJyYXJ5KGhlcmUpCiAjIF9fRGF0YSBXcmFuZ2xpbmcvTWFuaXB1bGF0aW9uX18KICMxLiBTZWxlY3RpbmcgY29sdW1ucwogIzIuIEZpbHRlcmluZyByb3dzCiAjMy4gQ3JlYXRpbmcgbmV3IGNvbHVtbnMKICM0LiBSZW5hbWluZyBjb2x1bW5zCiAjNS4gT3JkZXJpbmcgY29sdW1ucwogIzYuIE11dGF0aW5nIGNvbHVtbnMKICM3LiBEZWFsaW5nIHdpdGggTkFzCiAKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInBodC5wbmciKQpgYGAKCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KYm9keSB7CiAgY29sb3I6ICM2NjAwMDA7Cn0KYGBgCgoKYGBge3IgZmlndXJlbmFtZSwgZWNobz1GQUxTRSwgZmlnLmNhcD0iV2h5IENsaW5pY2FsIHRyaWFsIFNob3VsZCBFbWJyYWNlIHRoZSBSIGxhbmd1YWdlIiwgb3V0LndpZHRoID0gJzkwJScsZmlnLmhlaWdodD0xMn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImF3ZXNvbWVyLlBORyIpCmBgYAoKCiMjIFdoeSBpcyBkYXRhIG1hbmlwdWxhdGlvbiBpbXBvcnRhbnQ/CgotRGF0YSBtYW5pcHVsYXRpb24gaXMgdGhlIGNoYW5naW5nIG9mIGRhdGEgdG8gbWFrZSBpdCBlYXNpZXIgdG8gcmVhZCBvciBiZSBtb3JlIG9yZ2FuaXplZC4KCi1Db21wYW5pZXMgbWF5IG1hbmlwdWxhdGUgdGhlaXIgZGF0YSBiZWNhdXNlIGl0IGNhbiBwcm92aWRlIHRoZW0gd2l0aCB3ZWxsLW9yZ2FuaXplZCBkYXRhYmFzZXMuIENhdGVnb3JpemF0aW9uIGNhbiBhbGxvdyBjb21wYW5pZXMgdG8gZ3JvdXAgc2ltaWxhciBkYXRhLCB3aGljaCBtYXkgbWFrZSBpdCBlYXNpZXIgdG8gc2VhcmNoIGZvciBpbmZvcm1hdGlvbi4KCi1Bbm90aGVyIHJlYXNvbiBjb21wYW5pZXMgbWlnaHQgbWFuaXB1bGF0ZSB0aGVpciBkYXRhIGlzIHRoYXQgaXQgY2FuIGFsbG93IHRoZW0gdG8gYXJjaGl2ZSBwcm9qZWN0IGRhdGEgYW5kIGFjY2VzcyBpdCBsYXRlciBpZiB0aGV5IHdhbnQgdG8gdXNlIGl0IGFzIGEgcmVmZXJlbmNlIHdoaWxlIHdvcmtpbmcgb24gYSBuZXcgcHJvamVjdCBvciBzZXR0aW5nIGJ1c2luZXNzIGdvYWxzLgoKLURhdGEgbWFuaXB1bGF0aW9uIGlzIGFsc28gYSB2YWx1YWJsZSB0b29sIGluIGlkZW50aWZ5aW5nIGFuZCBjb3JyZWN0aW5nIGRhdGEgcmVkdW5kYW5jaWVzIGluIHJlcG9ydGluZy4KCmBgYHtjc3MsZWNobz1GfQouY29sdW1uc3tkaXNwbGF5OmZsZXg7fQpoMXtjb2xvcjpicm93bn0KaDJ7Y29sb3I6Ymx1ZX0KaDN7Y29sb3I6Z3JlZW59CgpgYGAKCgoKCiAKCiMjIF9fRGF0YSBjbGVhbmluZyBmdW5jdGlvbnNfXwoxLiBUaGUgZmlsdGVyKCkgZnVuY3Rpb24gc3Vic2V0cyB0aGUgcm93cyBpbiBhIGRhdGEgZnJhbWUgYnkgdGVzdGluZyBhZ2FpbnN0IGEgY29uZGl0aW9uYWwgc3RhdGVtZW50LgoKMi4gV2hlcmVhcyBmaWx0ZXIoKSBzdWJzZXRzIGEgZGF0YWZyYW1lIGJ5IHJvdywgc2VsZWN0KCkgcmV0dXJucyBhIHN1YnNldCBvZiB0aGUgY29sdW1ucy4KCjMuIG11dGF0ZSgpIGFkZHMgbmV3IGNvbHVtbnMgb2YgZGF0YS4KCjQuIGFycmFuZ2UoKSBpcyB0aGUgc2ltcGxlc3Qgb2YgdGhlIGRwbHlyIGZ1bmN0aW9ucywgd2hpY2ggb3JkZXJzIHJvd3MgYWNjb3JkaW5nIHRvIHZhbHVlcyBpbiBhIGdpdmVuIGNvbHVtbi4gVGhlIGRlZmF1bHQgaXMgdG8gb3JkZXIgbnVtYmVycyBmcm9tIGxvd2VzdCAtPiBoaWdoZXN0LgoKNS4gVGhlIHN1bW1hcml6ZSgpLCB3aGljaCBhcyB0aGUgbmFtZSBzdWdnZXN0cywgY3JlYXRlcyBpbmRpdmlkdWFsIHN1bW1hcnkgc3RhdGlzdGljcyBmcm9tIGxhcmdlciBkYXRhIHNldHMuCjYuIGdyb3VwX2J5KCkgYWxsb3dzIHVzIHRvIGNyZWF0ZSBzdWItZ3JvdXBzIGJhc2VkIG9uIGxhYmVscyBpbiBhIHBhcnRpY3VsYXIgY29sdW1uLCBhbmQgdG8gcnVuIHN1YnNlcXVlbnQgZnVuY3Rpb25zIG9uIGFsbCBzdWItZ3JvdXBzLgoKNy4gKCU+JSkgVGhlIHBvaW50IG9mIHRoZSBwaXBlIGlzIHRvIGhlbHAgeW91IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBpcyBlYXNpZXIgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZAoKCgoKIyMjICBfMS4gRE0gRG9tYWluLSBkYXRhIG1hbmlwdWxhdGlvbl8KIyMjIyBDaGVja2luZyBkYXRhIGFuZCByZWFkaW5nIGNvbHVtbnMgbmFtZXMKYGBge3J9CmxpYnJhcnkoeGFyaW5nYW50aGVtZXIpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGhhdmVuKSkKZG1kYXRhPWhhdmVuOjpyZWFkX3NhcygiZG0uc2FzN2JkYXQiKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKSNwYWNrYWdlIGZvciBkYXRhIG1hbmlwdWxhdGlvbgpuYW1lcyhkbWRhdGEpICAjY2hlY2tpbmcgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YQoKYGBgCgoKIyMjIyBDaGVja2luZyBkYXRhIHN0cnVjdHVyZQpgYGB7cn0KCmdsaW1wc2UoZG1kYXRhKSAjY2hlY2tpbmcgdGhlIGlmIHRoZSB2YXJpYWJsZSBpcyBudW1lcmljICxjaGFyYWN0ZXIgZXRjCmBgYAoKIyMjICBfX1NlbGVjdGluZyBudW1lcmljIGNvbHVtbnMgX18KYGBge3J9CmRtZGF0YSAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lCiAgbmFtZXMoKQpgYGAKCiMjIyAgX19TZWxlY3RpbmcgY2hhcmFjdGVyIGNvbHVtbnNfXyAKYGBge3J9CmRtZGF0YSAlPiUKICBzZWxlY3RfaWYoaXMuY2hhcmFjdGVyKSAlPiUKICBuYW1lcygpCmBgYAoKIyMjIF9fU2VsZWN0aW9uICBvZiAgY29sdW1ucyBieSBsb2dpY2FsIHN0YXRlbWVudF9fCgogVGhpcyBhbGxvdyB5b3UgdG8gc2VsZWN0IGEgY29sdW1uIGJ5IGEgZ2l2ZW4gY29uZGl0aW9uLiBJZiB0aGUgY29uZGl0aW9uIGlzIHRydWUgb3IgZmFsc2UuCmBgYHtyfQpkbWRhdGEgJT4lCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JQogIHNlbGVjdF9pZih+bWVhbiguLCBuYS5ybT1UUlVFKSA+IDUpICU+JSBoZWFkKDIpICU+JSBrbml0cjo6a2FibGUoKQojc2hvcnRlciB3YXkKZG1kYXRhICU+JQogIHNlbGVjdF9pZih+aXMubnVtZXJpYyguKSAmIG1lYW4oLiwgbmEucm09VFJVRSkgPiA1KSAlPiUgIGhlYWQoMikgJT4lIGtuaXRyOjprYWJsZSgpI3doZXJlIG1lYW4gaXMgbW9yZSB0aGFuIDUgYW5kIG9ubHkgbnVtZXJpYyB2YXJpYWJsZXMKYGBgCgojIyMgIF9fU2VsZWN0aW5nIGNvbHVtbnMgYmFzZWQgb24gcGFydGlhbCBjb2x1bW4gbmFtZXNfXwogSWYgeW91IGhhdmUgYSBsb3Qgb2YgY29sdW1ucyB3aXRoIGEgc2ltaWxhciBzdHJ1Y3R1cmUgeW91IGNhbiB1c2UgcGFydGlhbCBtYXRjaGluZyBieSBhZGRpbmcgX19zdGFydHNfd2l0aCgpLCBlbmRzX3dpdGgoKSBvciBjb250YWlucygpX18gaW4geW91ciBzZWxlY3Qgc3RhdGVtZW50LgpgYGB7cn0KZG1kYXRhICU+JQogIHNlbGVjdCggc3RhcnRzX3dpdGgoIkFDVCIpKSAlPiUjc2VsZWN0IGNvbHVtbnMgdGhhdCBzdGFydCB3aXRoIEFDVAogIG5hbWVzKCkgJT4lIGtuaXRyOjprYWJsZSgpCgojZW5kcwpkbWRhdGEgJT4lCiAgc2VsZWN0KGVuZHNfd2l0aCgiRFRDIikpICU+JSNzZWxlY3QgY29sdW1ucyBlbmRpbmcgd2l0aCBEVEMKICBjb2xuYW1lcygpICU+JSBrbml0cjo6a2FibGUoKQoKI2NvbnRhaW5zCmRtZGF0YSAlPiUKICBzZWxlY3QoY29udGFpbnMoIlJGWCIpKSU+JSNzZWxlY3QgY29sdW1ucyB0aGF0IGhhcyBSRlgKICBjb2xuYW1lcygpIApgYGAKCiAKIyMjIF9fU2VsZWN0aW5nIGNvbHVtbnMgYmFzZWQgb24gKioqcmVnZXggZXhwcmVzc2lvbioqKl9fCgpgYGB7cn0KZG1kYXRhICU+JQogIHNlbGVjdChtYXRjaGVzKCJSRlh8QVJNIikpICU+JQogIGdsaW1wc2UKYGBgCgoKIyMjIF9fU2VsZWN0aW5nIGNvbHVtbnMgYmFzZWQgcHJlLWlkZW50aWZpZWQgY29sdW1uc19fCmBgYHtyfQpjbGFzc2lmaWNhdGlvbiA8LSBjKCJTVFVEWUlEIiwgIkRPTUFJTiIsICJVU1VCSklEIiwgIlNVQkpJRCIsICJSRlNURFRDIiwgIlJGRU5EVEMiLCAKIlJGWFNURFRDIiwgIlJGWEVORFRDIiwgIlJGSUNEVEMiLCAiUkZQRU5EVEMiLCAiRFRIRFRDIiwgIkRUSEZMIiwgCiJTSVRFSUQiLCAiQlJUSERUQyIpCgpkbWRhdGEgJT4lCiAgc2VsZWN0KCEhY2xhc3NpZmljYXRpb24pICU+JSBuYW1lcygpCmBgYAoKCiMjIyBfX1NlbGVjdGluZyBjb2x1bW5zIGJhc2VkIG9uIGEgaGVscGVyIGZ1bmN0aW9ucyAqKipsYXN0X2NvbCoqKl9fCmBgYHtyfQpkbWRhdGEgJT4lIHNlbGVjdChsYXN0X2NvbCgwOjQpKQpgYGAKCiMjIyBfX01vdmluZyBhbmQgcmVhcnJhbmdpbmcgY29sdW1uc19fCmBgYHtyfQooZG1kYXRhICU+JSBzZWxlY3QobGFzdF9jb2woMCksZXZlcnl0aGluZygpKSkgJT4lIGhlYWQoMikjcHV0dGluZyB0aGUgbGFzdCBjb2x1bW5zIHRvIGJlIHRoZSBmaXJzdApgYGAKCgojIyMgIF9fRHJvcHBpbmcgZW1wdHkgY29sdW1ucyBfXwpgYGB7cn0KZG1kYXRhJE5VTExjb2w9TkEKZG1kYXRhICU+JXNlbGVjdF9pZih+IGFueShpcy5uYSguKSkpICU+JW5hbWVzKCkgJT4lIGtuaXRyOjprYWJsZSgpI2VtcHR5IGNvbHVtbgpkbWRhdGEgJT4lc2VsZWN0X2lmKH4hIGFueShpcy5uYSguKSkpICU+JW5hbWVzKCkgJT4lIGtuaXRyOjprYWJsZSgpI25vbi1lbXB0eSBjb2x1bW4Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoamFuaXRvcikpI2NhbGxpbmcgamFuaXRvciBwYWNrYWdlCiMoZGZ3PWRtZGF0YSAlPiUgcmVtb3ZlX2VtcHR5KCkgJT4lIGhlYWQoMikpICMgdGhlIGZ1Y250aW9uIGRyb3AgZW1wdHkgY29sdW1ucyBmcm9tIGRhdGEKIyhkZncxPWRtZGF0YSAlPiUgZGlzY2FyZCh+YWxsKGlzLm5hKC4pKSkgJT4lIGhlYWQoMikpIApgYGAKCgojIyMgX19EZXNlbGVjdGluZy9kZWxldGluZyBjb2x1bW5zIHdpdGggdmFsdWVzX18KCiBXZSB1c2UgYSBtaW51cyBzaWduIGJlZm9yZSB0aGUgdmFyaWFibGUgb3IgdGhlIHZlY3RvciBvZiB0aGUgdmFyaWFibGVzIHRvIGJlIGRyb3BwZWQKYGBge3J9CmRtZGF0YSAlPiUgCiAgc2VsZWN0KC1TVFVEWUlELCAtKERUSEZMOkFDVEFSTUNEKSkgJT4lCiAgbmFtZXMoKSAKYGBgCgoKCgojIyBEcm9wcGluZyB0aGUgbWlzc2luZyB2YWx1ZXMKICAgMSAqKm5hLm9taXQqKiB0aGUgY29tbWFuZCBkcm9wIGFsbCByb3dzIHdpdGggTkFzLGl0IGNvbWVzIGZyb20gYmFzZSByCiAgIAogICAyICoqY29tbHBldGUuY2FzZSoqIGl0IHdvcmtzIGluIHNpbWlsYXIgd2F5IGFzIG5hLm9taXQKICAgCiAgIDMgKipkcm9wX25hKiogdGhlIGZ1bmN0aW9uIGNvbWVzIGZyb20gKioqKmRwbHlyKioqIHBhY2thZ2UgaXQgd29ya3MgbGlrZSBuYS5vbWl0CiAgIAoKCiMjIyBfX0NoZWNraW5nIG51bGwgdmFsdWVzIGluIHJvd3NfXwpgYGB7cn0Kc2V0LnNlZWQoNDMyMSkKbXQgPC0gbWF0cml4KHNhbXBsZShjKE5BLCAxOjEwKSwgMTAwLCByZXBsYWNlID0gVFJVRSksIDEwKSNjcmVhdGluZyBhIG1hdHJpeCAoMTAgYnkgMTApCmR0IDwtIGFzLmRhdGEuZnJhbWUobXQpI2NvbnZlcnRpbmcgdGhlIG1hdHJpeCB0byBhIGRhdGFmcmFtZQpkdCNjYWxsaW5nIHRoZSBkYXRhCmBgYAoKCiMjIyBfX0Ryb3BwaW5nIG51bGwgb2JzZXJ2YXRpb25zX18KYGBge3J9CmNvbFN1bXMoaXMubmEoZHQpKQpkdFtpcy5uYShkdCldIDwtIDAjIGFzc2lnbmluZyB6ZXJvIHRvIE5BcwpkdCMgY2FsbGluZyB0aGUgbmV3IGRhdGEKY29sU3Vtcyhpcy5uYShkdCkpCmBgYAoKCiMjIyBEZWFsaW5nIHdpdGggdGhlIG51bGwgdmFsdWVzIGJ5IHJvd3MgCgpgYGB7cn0KZGYgPC0gZGF0YS5mcmFtZShYMSA9IGMoMToxMCksCiAgICAgICAgICAgICAgICAgICAgWDIgPSByZXAoTkEsIDEwKSwKICAgICAgICAgICAgICAgICAgICBYMyA9IGxldHRlcnNbc2VxKCBmcm9tID0gMSwgdG8gPSAxMCApXSwKICAgICAgICAgICAgICAgICAgICBYNCA9IHJlcCgiIiwxMCkpIyBjcmVhdGluZyBoeXBvdGhldGljYWwgZGF0YXNldCBpbiByCmRmCgpgYGAKCgojIyAgX19EZWFsaW5nIHdpdGggdGhlIG51bGwgdmFsdWVzIGJ5IHJvd3MgYW5kIGNvbHVtbnNfXwpgYGB7cn0KZGYxPW5hLm9taXQoZGYpI3dlIGRyb3AgYWxsIHRoZSBOQXMgdmFsdWVzCmRmMQpkZjI9ZGZbY29tcGxldGUuY2FzZXMoZGYpLF0jd2Uga2VlcCBvbmx5IGNvbXBsZXRlIGNhc2VzCmRmMgpgYGAKCiAKIyMjIF9fUmVtb3ZpbmcgTkFzIHVzaW5nICoqKnJvd1N1bXMqKiogZm4gZnJvbSBiYXNlIHJfXwpgYGB7cn0KbSA8LSBzdHJ1Y3R1cmUoYyggMSwgIE5BLCAgMywgIDQsICA1LCAKICAgICAgICAgICAgICAgICAgNiwgIE5BLCAgOCwgTkEsIDEwLCAKICAgICAgICAgICAgICAgICAxMSwgIE5BLCAxMywgTkEsIE5BKSwgCiAgICAgICAgICAgICAgIC5EaW0gPSBjKDVMLCAzTCkpCm0KKG1tPW1bcm93U3Vtcyhpcy5uYShtKSkgIT0gbmNvbChtKSwgXSkjd2UgZGVsZXRlIGFsbCByb3dzIHRoYXQgaGF2ZSBOQXMKCmBgYAoKCiMjIyBfX0RlbGV0aW5nIGVtcHR5IHJvd3MgdXNpbmcgYXBwbHkgZmFtaWx5IG9mIGZ1bmN0aW9ucyBmcm9tIGJhc2Ugcl9fCmBgYHtyfQppbmQgPC0gYXBwbHkobSwgMSwgZnVuY3Rpb24oeCkgYWxsKGlzLm5hKHgpKSkKbTE9bVshaW5kLF0KbTEKYGBgCgoKCiMjIyBNdXRhdGluZy9zZXBhcmF0aW5nL3VuaXRpbmcgY29sdW1ucwojIyMjIFRoZSBfX211dGF0ZV9fIGNvbW1hbmQgaXMgdXNlZCB3aGVuIHlvdSB3YW50IHRvIGNyZWF0ZSBhIG5ldyBjb2x1bW4gdG8gYmUgYWRkZWQgdG8gdGhlIGRhdGEgb3IgdG8gY2hhbmdlIHRoZSBkYXRhIHR5cGUuCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShsdWJyaWRhdGUpKSN3ZSBjYWxsIGx1YnJpZGF0ZSBhbmQgc3VwcHJlc3MgYW55IHdvcm5pbmcgb3IgbWVzc2FnZQpkbWRhdGEgJT4lIG11dGF0ZSh5ZWFyMT1zdWJzdHIoUkZTVERUQywxLDQpLHlyPXllYXIoUkZTVERUQyksaHI9aG91cihSRlNURFRDKSkgJT4lCiAgc2VsZWN0KHllYXIxLHlyLGhyKSAlPiUgaGVhZCgzKSAlPiUga25pdHI6OmthYmxlKCkKYGBgCgojIyMgTXV0YXRlX2lmCmBgYHtyfQpkbWRhdGEgJT4lCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIHJvdW5kLDEpICU+JXNlbGVjdF9pZih+aXMubnVtZXJpYyguKSkgJT4lCiAgaGVhZCgzKSAlPiUga25pdHI6OmthYmxlKCkKYGBgCgojIyMgVW5pdGluZyBjb2x1bW5zCiMjIyMgVGhlIF9fdW5pdGVfXyBmdW5jdGlvbiBmcm9tIF9fZHBseXJfXyBpcyB1c2VkIHRvIGNvbWJpbmUgY29sdW1ucwoKYGBge3J9CgpkZmZkPWRtZGF0YSAlPiUKICBzZWxlY3QoU1RVRFlJRCwgU1VCSklEKSAlPiV1bml0ZSgndXN1YmppZDEnLGMoU1RVRFlJRCxTVUJKSUQpLHNlcCA9ICIvIixyZW1vdmUgPSBGKSAlPiUgCiAgaGVhZCgzKQpkZmZkICU+JSBrbml0cjo6a2FibGUoKQpgYGAKCiMjIyBTZXBhcmF0aW5nIGNvbHVtbnMKIyMjIyBUaGUgX19zZXBhcmF0ZV9fIGZ1bmN0aW9uIGZyb20gX19kcGx5cl9fIGlzIHVzZWQgdG8gY29tYmluZSBjb2x1bW5zCmBgYHtyfQpkZmZkICU+JXNlcGFyYXRlKHVzdWJqaWQxLGMoJ1NUVURZSURfYScsJ1NVQkpJRF9iJyksc2VwID0gIi8iLHJlbW92ZSA9IEYpICU+JSAKICBoZWFkKDMpICU+JSBrbml0cjo6a2FibGUoKSNzZXBhcmF0aW5nIHRoZSB1c3ViamlkMSBieSBzZXBhcmF0b3IvCmBgYAoKIyMjIEZpbHRlcmluZwpgYGB7cn0KZG1kYXRhICU+JSBzZWxlY3QoVVNVQkpJRCxTRVgsRVRITklDKSAlPiUgZmlsdGVyKFNFWD09Ik0iICZVU1VCSklEICVpbiUgYygnQUItNTM2NS0xMDEtMDE3LTMwMDMnLCdBQi01MzY1LTEwMS0wMTgtMjAwNCcsJ0FCLTUzNjUtMTAxLTAxOS0yMDAzJykpICU+JSAKICBrbml0cjo6a2FibGUoKSAjc2VsZWN0IHRoZSBpZGVudGlmaWVkIHN1YmplY3QgYW5kIHRoZXkgc2hvdWxkIG5lIG1hbGUKZG1kYXRhICU+JSBzZWxlY3QoVVNVQkpJRCxTRVgsRVRITklDLEFHRSkgJT4lIGZpbHRlcihTRVg9PSJNIiAmIEFHRT43OCkgJT4lIAogIGtuaXRyOjprYWJsZSgpCgpgYGAKCgojIyMgX19Tb3J0aW5nLyBhcnJhbmdlL29yZGVyX18KYGBge3J9CmRtZGF0YSAlPiUgc2VsZWN0KFVTVUJKSUQsQUdFKSAlPiUgYXJyYW5nZShBR0UpICU+JSBoZWFkKDgpICAlPiUKICBrbml0cjo6a2FibGUoKSNhc2NlbmRpbmcKCmBgYAoKCmBgYHtyfQpkbWRhdGEgJT4lIHNlbGVjdChVU1VCSklELEFHRSkgJT4lIGFycmFuZ2UoZGVzYyhBR0UpKSAlPiUgaGVhZCg4KSAgJT4lIAogIGtuaXRyOjprYWJsZSgpI2Rlc2NlbmRpbmcgYWdlCmBgYAoKIAojIyMgX19SZW5hbWluZyBjb2x1bW5zX18KIyMjIyBUaGUgKioqcmVuYW1lKioqIGFuZCAqKipzZWxlY3QqKiogZnJvbSBkcGx5ciBhcmUgdXNlZCBpbiBjaGFuZ2luZyBjb2x1bW5zIG5hbWVzCmBgYHtyfQpkbWRhdGEgJT4lIHNlbGVjdChTVUJKSUQsU0VYLGNvbnRhaW5zKCdBQ1QnKSkgJT4lIHJlbmFtZShTdWJqZWN0PVNVQkpJRCxHZW5kZXI9U0VYKSAlPiUgaGVhZCgzKSU+JSAKICBrbml0cjo6a2FibGUoKSAjd2UgdXNlIHJlbmFtZSB0byBjaGFuZ2UgY29sdW1ucyBuYW1lCmRtZGF0YSAlPiUgc2VsZWN0KFN1YmplY3QxPVNVQkpJRCxHZW5kZXIxPVNFWCxjb250YWlucygnQUNUJykpICU+JSAgaGVhZCgzKSU+JSAKICBrbml0cjo6a2FibGUoKQpgYGAKCgojIyMgX19CYXNlIHIgcmVuYW1pbmdfXwpgYGB7cn0KKG5hbWVzKGRtZGF0YSlbNF09KCdTQkpEJykpCmBgYAoKCgojIyMgICoqZ3JvdXBfYnkoKSAlPiUgc3VtbWFyaXplKCkqKgpgYGB7cn0KZG1kYXRhICU+JSAgZ3JvdXBfYnkoQVJNKSAlPiUgICBzdW1tYXJpc2UoQXZlcmFnZSA9IG1lYW4oQUdFKSxzZHY9c2QoQUdFKSxtZWRpYW5fPW1lZGlhbihBR0UpKSU+JSAKICBrbml0cjo6a2FibGUoKQpgYGAK