Note that the directories used to store data are likely different on your computer, and such references will need to be changed before using any such code.

# Google ngrams for Data TODO
There were 30 warnings (use warnings() to see them)
library(ngramr)
library(ggplot2)
ngd <- c("(Analytics + analytics)", "(Machine learning + machine learning)")
ggram(ngd, year_start=1960, geom = "area", google_theme=F, smoothing = 3) + theme(legend.position="bottom", legend.direction="horizontal")

library(readr)
df <- read_csv("../../Data/Session_1-1.csv")
df <- df[df$indfmt != "FS",]  # remove FS codes
df <- df[order(df$gvkey,df$datadate),]
df$ni_lag <- c(NA, df$ni[1:nrow(df)-1])
df <- df[floor(df$datadate/10000) == 2017,]
df <- df[!(is.na(df$ni) | is.na(df$ni_lag)),]
library(ggplot2)
suppressPackageStartupMessages(library(plotly))
cor <- cor(x=df$ni, y=df$ni_lag, method="pearson")
set.seed(11)
df_s <- df[sample(nrow(df), 400),]
plot <- ggplot(df_s, aes(x=ni_lag, y=ni)) + 
  geom_point(shape=1, aes(text=sprintf("Ticker: %s", tic))) + 
  geom_smooth(aes(x=ni_lag, y=ni), method=lm, se=T) + 
  labs(x="2016 Net Income ($M USD)",
       y="2017 Net Income ($M USD)")
ggplotly(plot, tooltip="text")
library(readr)
# import GDP data
dfg <- read_csv("../../Data/per-capita-gni-and-per-capita-gdp-at-current-market-prices-annual-2019.08.csv")
dfg <- dfg[dfg$level_1 == "Per Capita GDP",]
dfg <- dfg[order(dfg$year),]

# import unemployment data
dfu <- read_csv("../../Data/overall-unemployment-rate-annual-2019.08.csv")
dfu <- dfu[order(dfu$year),]

# merge into one dataset
df <- data.frame(year=1993:2016,
                 growth=dfg[dfg$year>1992,]$value / dfg[dfg$year > 1991 & dfg$year < 2016,]$value - 1,
                 unemploy=dfu[dfu$year>1992,]$unemployment_rate,
                 unemploy_lag=dfu[dfu$year<2016,]$unemployment_rate)

library(ggplot2)
suppressPackageStartupMessages(library(plotly))
cor <- cor(x=df$growth, y=df$unemploy, method="pearson")
plot <- ggplot(df, aes(x=unemploy, y=growth)) + 
  geom_point(shape=1, aes(text=sprintf("Year: %i", year))) + 
  geom_smooth(method=lm, se=T) + 
  labs(x="Unemployment rate",
       y="GDP Growth")
ggplotly(plot, tooltip="text")
drown <- c(109,102,102,98,85,95,96,98,123,94,102) # US CDC data, 1999 to 2009 (falling in)
drown2 <- c(421,465,494,538,430,530,511,600,582,605,603)
ic <- c(16.1, 15.8, 16.2, 15.9 ,14.6 ,15.1 ,15.3, 14.8, 14.2 ,13.9 ,14 ,13.2 ,13.2 ,13 ,12.5 ,12.9 ,13.1) # Statista, 2000 to 2016
icprice <- c(126.858,128.467,130.858,131.733,134.808,137.508,144.592,150.575,155.500,161.742,164.375,173.408,179.033,175.467,178.250,177.575,179.233,183.373,192.768,196.637,195.055,209.161,215.524,215.489,215.412,219.201,219.452,219.903,219.593) # CPI data for ice cream and related products, 1990-2018
cancerthursday <- c(80262,80994,81321,81988,81390,82682,82109,82516,82663,83166,84978,84742) # CDC (tylervigen.com)
# https://futurism.com/correlation-vs-causation-2/
df <- data.frame(drown=drown2[2:length(drown2)],
                 icecream=ic[1 : length(drown2)-1],
                 icecreamprice=icprice[11:(length(drown2)+9)],
                 year=2000:2009)
df$drown <- df$drown / mean(df$drown)
df$icecream <- df$icecream / mean(df$icecream)
df$icecream2 <- df$icecream*df$icecreamprice / mean(df$icecream*df$icecreamprice)
df$icecream3 <- df$icecreamprice / mean(df$icecreamprice)
cor_unit <- cor(x=df$icecream, y=df$drown)
cor_rev <- cor(x=df$icecream2, y=df$drown)
cor_price <- cor(x=df$icecreamprice, y=df$drown)
library(ggplot2)

library(reshape2)
dm <- melt(df[c("drown", "icecream2", "year")],id.var=3)
plot1 <- ggplot(dm, aes(x=year, y=value, colour=variable)) + 
  geom_point(shape=1) +
  geom_smooth(method=loess, se=F) +
  labs(x="Year",
       y="% of average year") + 
  scale_x_continuous(breaks=c(2000,2003,2006,2009)) + 
  scale_colour_discrete(name="Statistic",
                        breaks=c("icecream2","drown"),
                        labels=c("Ice cream revenue","Drowning in pools"))
dm <- melt(df[c("drown", "icecream3", "year")],id.var=3)
plot2 <- ggplot(dm, aes(x=year, y=value, colour=variable)) + 
  geom_point(shape=1) +
  geom_smooth(method=loess, se=F) +
  labs(x="Year",
       y="% of average year") + 
  scale_x_continuous(breaks=c(2000,2003,2006,2009)) + 
  scale_colour_discrete(name="Statistic",
                        breaks=c("icecream3","drown"),
                        labels=c("Ice cream volume sold","Drowning in pools"))
dm <- melt(df[c("drown", "icecream", "year")],id.var=3)
plot3 <- ggplot(dm, aes(x=year, y=value, colour=variable)) + 
  geom_point(shape=1) +
  geom_smooth(method=loess, se=F) +
  labs(x="Year",
       y="% of average year") + 
  scale_x_continuous(breaks=c(2000,2003,2006,2009)) + 
  scale_colour_discrete(name="Statistic",
                        breaks=c("icecream","drown"),
                        labels=c("Ice cream price","Drowning in pools"))
suppressPackageStartupMessages(library(plotly))
ggplotly(plot1)
suppressPackageStartupMessages(library(plotly))
ggplotly(plot3)
suppressPackageStartupMessages(library(plotly))
ggplotly(plot2)
# Addition uses '+'
1 + 1
[1] 2
# Subtraction uses '-'
2 - 1
[1] 1
# Multiplication uses '*'
3 * 3
[1] 9
# Division uses '/'
4 / 2
[1] 2
# Exponentiation uses '^'
5 ^ 5
[1] 3125
# Modulus (aka the remainder) uses '%%'
46 %% 6
[1] 4
# Integer division uses '%/%'
46 %/% 6
[1] 7
# Store 2 in 'x'
x <- 2

# Check the value of x
x
[1] 2
# Store arithmetic in y
y <- x * 2

# Check the value of y
y
[1] 4
# Previous value of x and y
paste(x, y)
[1] "2 4"
# Change x, then recheck the value
# of x and y
x <- 200

paste(x, y)
[1] "200 4"
# Data from Singtel's earnings reports, in Millions of SGD
singtel_2017 <- 3831.0
singtel_2018 <- 5430.3

# Compute growth
growth <- singtel_2018 / singtel_2017 - 1

# Check the value of growth
growth
[1] 0.4174628
company_name <- "Google"  # character
company_name
[1] "Google"
company_name <- 'Google'  # character
company_name
[1] "Google"
tech_firm <- TRUE  # boolean
tech_firm
[1] TRUE
earnings <- 12662  # numeric, $M USD
earnings
[1] 12662
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
v tibble  3.0.3     v dplyr   1.0.1
v tidyr   1.1.1     v stringr 1.4.0
v purrr   0.3.4     v forcats 0.5.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks plotly::filter(), stats::filter()
x dplyr::lag()    masks stats::lag()
df <- read.csv("../../Data/Session_1-2.csv", stringsAsFactors=FALSE)
# Filter to firms with at least $1M USD revenue, known net income, and fiscal year of 2017
clean_df <- df %>% filter(fyear==2017, !is.na(revt), !is.na(ni), revt > 1)
tech_df <- clean_df %>%
  filter(gsector==45) %>%
  mutate(revenue = revt,
         earnings = ni,
         margin = ni/revt)
earnings_2017 <- tech_df$ni
revenue_2017 <- tech_df$revt
names_2017 <- tech_df$conm
names(earnings_2017) <- names_2017
names(revenue_2017) <- names_2017
# Calculating proit margin for all public US tech firms
# 715 tech firms in Compustat with >1M sales in 2017

# Data:
#    earnings_2017: vector of earnings, $M USD
#    revenue_2017: vector of revenue, $M USD
#    names_2017: a vector of tickers (strings)

# Namining the vectors
names(earnings_2017) <- names_2017
names(revenue_2017) <- names_2017

earnings_2017[1:6]
              AVX CORP        BK TECHNOLOGIES ADVANCED MICRO DEVICES 
                 4.910                 -3.626                 43.000 
  ASM INTERNATIONAL NV SKYWORKS SOLUTIONS INC         ANALOG DEVICES 
               543.878               1010.200                727.259 
revenue_2017[1:6]
              AVX CORP        BK TECHNOLOGIES ADVANCED MICRO DEVICES 
              1562.474                 39.395               5329.000 
  ASM INTERNATIONAL NV SKYWORKS SOLUTIONS INC         ANALOG DEVICES 
               886.503               3651.400               5107.503 
# Summarizing vectors
summary(earnings_2017)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-4307.49   -15.98     1.84   296.84    91.36 48351.00 
summary(revenue_2017)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
     1.06    102.62    397.57   3023.78   1531.59 229234.00 
# Calculating profit margin
margin <- earnings_2017 / revenue_2017
summary(margin)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-13.97960  -0.10253   0.01353  -0.10967   0.09295   1.02655 
# Worst, midpoint, and best profit margin firms in 2017. Our names carried over :)
margin[order(margin)][c(1,length(margin)/2,length(margin))]
HELIOS AND MATHESON ANALYTIC                   NLIGHT INC            CCUR HOLDINGS INC 
                -13.97960161                   0.01325588                   1.02654899 
columns <- c("Google", "Microsoft",
             "Goldman")
rows <- c("Earnings","Revenue")

firm_data <- matrix(data=
  c(12662, 21204, 4286, 110855,
    89950, 42254), nrow=2)
# Equivalent:
# matrix(data=c(12662, 21204, 4286,
#   110855, 89950, 42254), ncol=3)

# Apply names
rownames(firm_data) <- rows
colnames(firm_data) <- columns

# Print the matrix
firm_data
         Google Microsoft Goldman
Earnings  12662      4286   89950
Revenue   21204    110855   42254
firm_data[2, 3]
[1] 42254
firm_data[, c("Google", "Microsoft")]
         Google Microsoft
Earnings  12662      4286
Revenue   21204    110855
firm_data[1,]
   Google Microsoft   Goldman 
    12662      4286     89950 
firm_data["Revenue", "Goldman"]
[1] 42254
indcode <- c(45,45,40)
jpdata <- c(17370, 115475)
# Preloaded: industry codes as indcode (vector)
# Preloaded: industry codes as indcode (vector)
#     - GICS codes: 40=Financials, 45=Information Technology
#     - See: https://en.wikipedia.org/wiki/Global_Industry_Classification_Standard
# Preloaded: JPMorgan data as jpdata (vector)

mat <- rbind(firm_data,indcode)  # Add a row
rownames(mat)[3] <- "Industry"  # Name the new row
mat
         Google Microsoft Goldman
Earnings  12662      4286   89950
Revenue   21204    110855   42254
Industry     45        45      40
mat <- cbind(firm_data,jpdata)  # Add a column
colnames(mat)[4] <- "JPMorgan"  # Name the new column
mat
         Google Microsoft Goldman JPMorgan
Earnings  12662      4286   89950    17370
Revenue   21204    110855   42254   115475
# Ignore this code for now...
model <- summary(lm(earnings ~ revenue, data=tech_df))
#Note that this function is hiding something...
model

Call:
lm(formula = earnings ~ revenue, data = tech_df)

Residuals:
     Min       1Q   Median       3Q      Max 
-16045.0     20.0    141.6    177.1  12104.6 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -1.837e+02  4.491e+01  -4.091 4.79e-05 ***
revenue      1.589e-01  3.564e-03  44.585  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1166 on 713 degrees of freedom
Multiple R-squared:  0.736, Adjusted R-squared:  0.7356 
F-statistic:  1988 on 1 and 713 DF,  p-value: < 2.2e-16
model["r.squared"]
$r.squared
[1] 0.7360059
model[["r.squared"]]
[1] 0.7360059
model$r.squared
[1] 0.7360059
earnings <- c(12662, 21204, 4286)
company <- c("Google", "Microsoft", "Goldman")
names(earnings) <- company
earnings["Google"]
Google 
 12662 
earnings[["Google"]]
[1] 12662
#Can't use $ with vectors
str(model)
List of 11
 $ call         : language lm(formula = earnings ~ revenue, data = tech_df)
 $ terms        :Classes 'terms', 'formula'  language earnings ~ revenue
  .. ..- attr(*, "variables")= language list(earnings, revenue)
  .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : chr [1:2] "earnings" "revenue"
  .. .. .. ..$ : chr "revenue"
  .. ..- attr(*, "term.labels")= chr "revenue"
  .. ..- attr(*, "order")= int 1
  .. ..- attr(*, "intercept")= int 1
  .. ..- attr(*, "response")= int 1
  .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. ..- attr(*, "predvars")= language list(earnings, revenue)
  .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric"
  .. .. ..- attr(*, "names")= chr [1:2] "earnings" "revenue"
 $ residuals    : Named num [1:715] -59.7 173.8 -620.2 586.7 613.6 ...
  ..- attr(*, "names")= chr [1:715] "1" "2" "3" "4" ...
 $ coefficients : num [1:2, 1:4] -1.84e+02 1.59e-01 4.49e+01 3.56e-03 -4.09 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:2] "(Intercept)" "revenue"
  .. ..$ : chr [1:4] "Estimate" "Std. Error" "t value" "Pr(>|t|)"
 $ aliased      : Named logi [1:2] FALSE FALSE
  ..- attr(*, "names")= chr [1:2] "(Intercept)" "revenue"
 $ sigma        : num 1166
 $ df           : int [1:3] 2 713 2
 $ r.squared    : num 0.736
 $ adj.r.squared: num 0.736
 $ fstatistic   : Named num [1:3] 1988 1 713
  ..- attr(*, "names")= chr [1:3] "value" "numdf" "dendf"
 $ cov.unscaled : num [1:2, 1:2] 1.48e-03 -2.83e-08 -2.83e-08 9.35e-12
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:2] "(Intercept)" "revenue"
  .. ..$ : chr [1:2] "(Intercept)" "revenue"
 - attr(*, "class")= chr "summary.lm"
library(tidyverse)  # Imports most tidy packages
# Base R data import -- stringsAsFactors is important here
df <- read.csv("../../Data/Session_1-2.csv", stringsAsFactors=FALSE)
df <- subset(df, fyear == 2017 & !is.na(revt) & !is.na(ni) &
             revt > 1 & gsector == 45)
df$margin = df$ni / df$revt
summary(df)
     gvkey           datadate            fyear         indfmt         
 Min.   :  1072   Min.   :20170630   Min.   :2017   Length:715        
 1st Qu.: 20231   1st Qu.:20171231   1st Qu.:2017   Class :character  
 Median : 33232   Median :20171231   Median :2017   Mode  :character  
 Mean   : 79699   Mean   :20172029   Mean   :2017                     
 3rd Qu.:148393   3rd Qu.:20171231   3rd Qu.:2017                     
 Max.   :315629   Max.   :20180430   Max.   :2017                     
                                                                      
    consol             popsrc            datafmt              tic           
 Length:715         Length:715         Length:715         Length:715        
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                            
                                                                            
                                                                            
                                                                            
     conm              curcd                 ni                revt          
 Length:715         Length:715         Min.   :-4307.49   Min.   :     1.06  
 Class :character   Class :character   1st Qu.:  -15.98   1st Qu.:   102.62  
 Mode  :character   Mode  :character   Median :    1.84   Median :   397.57  
                                       Mean   :  296.84   Mean   :  3023.78  
                                       3rd Qu.:   91.36   3rd Qu.:  1531.59  
                                       Max.   :48351.00   Max.   :229234.00  
                                                                             
      cik             costat               gind           gsector      gsubind        
 Min.   :   2186   Length:715         Min.   :451010   Min.   :45   Min.   :45101010  
 1st Qu.: 887604   Class :character   1st Qu.:451020   1st Qu.:45   1st Qu.:45102020  
 Median :1102307   Mode  :character   Median :451030   Median :45   Median :45103020  
 Mean   :1086969                      Mean   :451653   Mean   :45   Mean   :45165290  
 3rd Qu.:1405497                      3rd Qu.:452030   3rd Qu.:45   3rd Qu.:45203012  
 Max.   :1725579                      Max.   :453010   Max.   :45   Max.   :45301020  
 NA's   :3                                                                            
     margin         
 Min.   :-13.97960  
 1st Qu.: -0.10253  
 Median :  0.01353  
 Mean   : -0.10967  
 3rd Qu.:  0.09295  
 Max.   :  1.02655  
                    
# Tidy import
df <- read_csv("../../Data/Session_1-2.csv") %>%
  filter(fyear == 2017,      # fiscal year
         !is.na(revt),       # revenue not missing
         !is.na(ni),         # net income not missing
         revt > 1,           # at least 1M USD in revenue
         gsector == 45) %>%  # tech firm
  mutate(margin = ni/revt)   # profit margin
summary(df)
    gvkey              datadate            fyear         indfmt         
 Length:715         Min.   :20170630   Min.   :2017   Length:715        
 Class :character   1st Qu.:20171231   1st Qu.:2017   Class :character  
 Mode  :character   Median :20171231   Median :2017   Mode  :character  
                    Mean   :20172029   Mean   :2017                     
                    3rd Qu.:20171231   3rd Qu.:2017                     
                    Max.   :20180430   Max.   :2017                     
    consol             popsrc            datafmt              tic           
 Length:715         Length:715         Length:715         Length:715        
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                            
                                                                            
                                                                            
     conm              curcd                 ni                revt          
 Length:715         Length:715         Min.   :-4307.49   Min.   :     1.06  
 Class :character   Class :character   1st Qu.:  -15.98   1st Qu.:   102.62  
 Mode  :character   Mode  :character   Median :    1.84   Median :   397.57  
                                       Mean   :  296.84   Mean   :  3023.78  
                                       3rd Qu.:   91.36   3rd Qu.:  1531.59  
                                       Max.   :48351.00   Max.   :229234.00  
     cik               costat               gind           gsector      gsubind        
 Length:715         Length:715         Min.   :451010   Min.   :45   Min.   :45101010  
 Class :character   Class :character   1st Qu.:451020   1st Qu.:45   1st Qu.:45102020  
 Mode  :character   Mode  :character   Median :451030   Median :45   Median :45103020  
                                       Mean   :451653   Mean   :45   Mean   :45165290  
                                       3rd Qu.:452030   3rd Qu.:45   3rd Qu.:45203012  
                                       Max.   :453010   Max.   :45   Max.   :45301020  
     margin         
 Min.   :-13.97960  
 1st Qu.: -0.10253  
 Median :  0.01353  
 Mean   : -0.10967  
 3rd Qu.:  0.09295  
 Max.   :  1.02655  
df <- mutate(
        filter(
          read_csv("../../Data/Session_1-2.csv"),
          fyear == 2017,      # fiscal year
          !is.na(revt),       # revenue not missing
          !is.na(ni),         # net income not missing
          revt > 1,           # at least 1M USD in revenue
          gsector == 45),  # tech firm
        margin = ni/revt)   # profit margin
summary(df)
    gvkey              datadate            fyear         indfmt         
 Length:715         Min.   :20170630   Min.   :2017   Length:715        
 Class :character   1st Qu.:20171231   1st Qu.:2017   Class :character  
 Mode  :character   Median :20171231   Median :2017   Mode  :character  
                    Mean   :20172029   Mean   :2017                     
                    3rd Qu.:20171231   3rd Qu.:2017                     
                    Max.   :20180430   Max.   :2017                     
    consol             popsrc            datafmt              tic           
 Length:715         Length:715         Length:715         Length:715        
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                            
                                                                            
                                                                            
     conm              curcd                 ni                revt          
 Length:715         Length:715         Min.   :-4307.49   Min.   :     1.06  
 Class :character   Class :character   1st Qu.:  -15.98   1st Qu.:   102.62  
 Mode  :character   Mode  :character   Median :    1.84   Median :   397.57  
                                       Mean   :  296.84   Mean   :  3023.78  
                                       3rd Qu.:   91.36   3rd Qu.:  1531.59  
                                       Max.   :48351.00   Max.   :229234.00  
     cik               costat               gind           gsector      gsubind        
 Length:715         Length:715         Min.   :451010   Min.   :45   Min.   :45101010  
 Class :character   Class :character   1st Qu.:451020   1st Qu.:45   1st Qu.:45102020  
 Mode  :character   Mode  :character   Median :451030   Median :45   Median :45103020  
                                       Mean   :451653   Mean   :45   Mean   :45165290  
                                       3rd Qu.:452030   3rd Qu.:45   3rd Qu.:45203012  
                                       Max.   :453010   Max.   :45   Max.   :45301020  
     margin         
 Min.   :-13.97960  
 1st Qu.: -0.10253  
 Median :  0.01353  
 Mean   : -0.10967  
 3rd Qu.:  0.09295  
 Max.   :  1.02655  
# To see the arguments a function takes, run:
args(data.frame)
function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, 
    fix.empty.names = TRUE, stringsAsFactors = default.stringsAsFactors()) 
NULL
add_two <- function(n) {
  n + 2
}

add_two(500)
[1] 502
mult_together <- function(n1, n2=0, square=FALSE) {
  if (!square) {
    n1 * n2
  } else {
    n1 * n1
  }
}

mult_together(5,6)
[1] 30
mult_together(5,6,square=TRUE)
[1] 25
mult_together(5,square=TRUE)
[1] 25
FXRate <- function(from="USD", to="SGD", dt=Sys.Date()) {
  options("getSymbols.warning4.0"=FALSE)
  require(quantmod)
  data <- getSymbols(paste0(from, "/", to), from=dt-3, to=dt, src="oanda", auto.assign=F)
  return(data[[1]])
}
date()
[1] "Mon Aug 17 01:54:47 2020"
FXRate(from="USD", to="SGD")  # Today's SGD to USD rate
[1] 1.371802
FXRate(from="SGD", to="CNY")  # Today's SGD to CNY rate
[1] 5.065638
FXRate(from="USD", to="SGD", dt=Sys.Date()-90)  # Last quarter's SGD to USD rate
[1] 1.42746
LS0tDQp0aXRsZTogIkNvZGUgZm9yIFNlc3Npb24gMSINCmF1dGhvcjogIkRyLiBSaWNoYXJkIE0uIENyb3dsZXkiDQpkYXRlOiAiIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rDQotLS0NCg0KTm90ZSB0aGF0IHRoZSBkaXJlY3RvcmllcyB1c2VkIHRvIHN0b3JlIGRhdGEgYXJlIGxpa2VseSBkaWZmZXJlbnQgb24geW91ciBjb21wdXRlciwgYW5kIHN1Y2ggcmVmZXJlbmNlcyB3aWxsIG5lZWQgdG8gYmUgY2hhbmdlZCBiZWZvcmUgdXNpbmcgYW55IHN1Y2ggY29kZS4NCg0KYGBge3IsIGV2YWw9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgR29vZ2xlIG5ncmFtcyBmb3IgRGF0YSBUT0RPDQpsaWJyYXJ5KG5ncmFtcikNCmxpYnJhcnkoZ2dwbG90MikNCm5nZCA8LSBjKCIoQW5hbHl0aWNzICsgYW5hbHl0aWNzKSIsICIoTWFjaGluZSBsZWFybmluZyArIG1hY2hpbmUgbGVhcm5pbmcpIikNCmBgYA0KDQpgYGB7ciwgZXZhbD1UUlVFLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0yLjh9DQpnZ3JhbShuZ2QsIHllYXJfc3RhcnQ9MTk2MCwgZ2VvbSA9ICJhcmVhIiwgZ29vZ2xlX3RoZW1lPUYsIHNtb290aGluZyA9IDMpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikNCmBgYA0KDQpgYGB7ciBncmFwaDEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA0LjN9DQpsaWJyYXJ5KHJlYWRyKQ0KZGYgPC0gcmVhZF9jc3YoIi4uLy4uL0RhdGEvU2Vzc2lvbl8xLTEuY3N2IikNCmRmIDwtIGRmW2RmJGluZGZtdCAhPSAiRlMiLF0gICMgcmVtb3ZlIEZTIGNvZGVzDQpkZiA8LSBkZltvcmRlcihkZiRndmtleSxkZiRkYXRhZGF0ZSksXQ0KZGYkbmlfbGFnIDwtIGMoTkEsIGRmJG5pWzE6bnJvdyhkZiktMV0pDQpkZiA8LSBkZltmbG9vcihkZiRkYXRhZGF0ZS8xMDAwMCkgPT0gMjAxNyxdDQpkZiA8LSBkZlshKGlzLm5hKGRmJG5pKSB8IGlzLm5hKGRmJG5pX2xhZykpLF0NCmxpYnJhcnkoZ2dwbG90MikNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpDQpjb3IgPC0gY29yKHg9ZGYkbmksIHk9ZGYkbmlfbGFnLCBtZXRob2Q9InBlYXJzb24iKQ0Kc2V0LnNlZWQoMTEpDQpkZl9zIDwtIGRmW3NhbXBsZShucm93KGRmKSwgNDAwKSxdDQpgYGANCg0KYGBge3IgZ3JhcGgxX3BhcnQyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNC4zfQ0KcGxvdCA8LSBnZ3Bsb3QoZGZfcywgYWVzKHg9bmlfbGFnLCB5PW5pKSkgKyANCiAgZ2VvbV9wb2ludChzaGFwZT0xLCBhZXModGV4dD1zcHJpbnRmKCJUaWNrZXI6ICVzIiwgdGljKSkpICsgDQogIGdlb21fc21vb3RoKGFlcyh4PW5pX2xhZywgeT1uaSksIG1ldGhvZD1sbSwgc2U9VCkgKyANCiAgbGFicyh4PSIyMDE2IE5ldCBJbmNvbWUgKCRNIFVTRCkiLA0KICAgICAgIHk9IjIwMTcgTmV0IEluY29tZSAoJE0gVVNEKSIpDQpnZ3Bsb3RseShwbG90LCB0b29sdGlwPSJ0ZXh0IikNCmBgYA0KDQpgYGB7ciBncmFwaDIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHJlYWRyKQ0KIyBpbXBvcnQgR0RQIGRhdGENCmRmZyA8LSByZWFkX2NzdigiLi4vLi4vRGF0YS9wZXItY2FwaXRhLWduaS1hbmQtcGVyLWNhcGl0YS1nZHAtYXQtY3VycmVudC1tYXJrZXQtcHJpY2VzLWFubnVhbC0yMDE5LjA4LmNzdiIpDQpkZmcgPC0gZGZnW2RmZyRsZXZlbF8xID09ICJQZXIgQ2FwaXRhIEdEUCIsXQ0KZGZnIDwtIGRmZ1tvcmRlcihkZmckeWVhciksXQ0KDQojIGltcG9ydCB1bmVtcGxveW1lbnQgZGF0YQ0KZGZ1IDwtIHJlYWRfY3N2KCIuLi8uLi9EYXRhL292ZXJhbGwtdW5lbXBsb3ltZW50LXJhdGUtYW5udWFsLTIwMTkuMDguY3N2IikNCmRmdSA8LSBkZnVbb3JkZXIoZGZ1JHllYXIpLF0NCg0KIyBtZXJnZSBpbnRvIG9uZSBkYXRhc2V0DQpkZiA8LSBkYXRhLmZyYW1lKHllYXI9MTk5MzoyMDE2LA0KICAgICAgICAgICAgICAgICBncm93dGg9ZGZnW2RmZyR5ZWFyPjE5OTIsXSR2YWx1ZSAvIGRmZ1tkZmckeWVhciA+IDE5OTEgJiBkZmckeWVhciA8IDIwMTYsXSR2YWx1ZSAtIDEsDQogICAgICAgICAgICAgICAgIHVuZW1wbG95PWRmdVtkZnUkeWVhcj4xOTkyLF0kdW5lbXBsb3ltZW50X3JhdGUsDQogICAgICAgICAgICAgICAgIHVuZW1wbG95X2xhZz1kZnVbZGZ1JHllYXI8MjAxNixdJHVuZW1wbG95bWVudF9yYXRlKQ0KDQpsaWJyYXJ5KGdncGxvdDIpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShwbG90bHkpKQ0KY29yIDwtIGNvcih4PWRmJGdyb3d0aCwgeT1kZiR1bmVtcGxveSwgbWV0aG9kPSJwZWFyc29uIikNCg0KDQpgYGANCg0KYGBge3IgZ3JhcGgyX3BhcnQyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNC4zfQ0KcGxvdCA8LSBnZ3Bsb3QoZGYsIGFlcyh4PXVuZW1wbG95LCB5PWdyb3d0aCkpICsgDQogIGdlb21fcG9pbnQoc2hhcGU9MSwgYWVzKHRleHQ9c3ByaW50ZigiWWVhcjogJWkiLCB5ZWFyKSkpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgc2U9VCkgKyANCiAgbGFicyh4PSJVbmVtcGxveW1lbnQgcmF0ZSIsDQogICAgICAgeT0iR0RQIEdyb3d0aCIpDQpnZ3Bsb3RseShwbG90LCB0b29sdGlwPSJ0ZXh0IikNCmBgYA0KDQpgYGB7ciBncmFwaDMsIGV2YWw9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmRyb3duIDwtIGMoMTA5LDEwMiwxMDIsOTgsODUsOTUsOTYsOTgsMTIzLDk0LDEwMikgIyBVUyBDREMgZGF0YSwgMTk5OSB0byAyMDA5IChmYWxsaW5nIGluKQ0KZHJvd24yIDwtIGMoNDIxLDQ2NSw0OTQsNTM4LDQzMCw1MzAsNTExLDYwMCw1ODIsNjA1LDYwMykNCmljIDwtIGMoMTYuMSwgMTUuOCwgMTYuMiwgMTUuOSAsMTQuNiAsMTUuMSAsMTUuMywgMTQuOCwgMTQuMiAsMTMuOSAsMTQgLDEzLjIgLDEzLjIgLDEzICwxMi41ICwxMi45ICwxMy4xKSAjIFN0YXRpc3RhLCAyMDAwIHRvIDIwMTYNCmljcHJpY2UgPC0gYygxMjYuODU4LDEyOC40NjcsMTMwLjg1OCwxMzEuNzMzLDEzNC44MDgsMTM3LjUwOCwxNDQuNTkyLDE1MC41NzUsMTU1LjUwMCwxNjEuNzQyLDE2NC4zNzUsMTczLjQwOCwxNzkuMDMzLDE3NS40NjcsMTc4LjI1MCwxNzcuNTc1LDE3OS4yMzMsMTgzLjM3MywxOTIuNzY4LDE5Ni42MzcsMTk1LjA1NSwyMDkuMTYxLDIxNS41MjQsMjE1LjQ4OSwyMTUuNDEyLDIxOS4yMDEsMjE5LjQ1MiwyMTkuOTAzLDIxOS41OTMpICMgQ1BJIGRhdGEgZm9yIGljZSBjcmVhbSBhbmQgcmVsYXRlZCBwcm9kdWN0cywgMTk5MC0yMDE4DQpjYW5jZXJ0aHVyc2RheSA8LSBjKDgwMjYyLDgwOTk0LDgxMzIxLDgxOTg4LDgxMzkwLDgyNjgyLDgyMTA5LDgyNTE2LDgyNjYzLDgzMTY2LDg0OTc4LDg0NzQyKSAjIENEQyAodHlsZXJ2aWdlbi5jb20pDQojIGh0dHBzOi8vZnV0dXJpc20uY29tL2NvcnJlbGF0aW9uLXZzLWNhdXNhdGlvbi0yLw0KZGYgPC0gZGF0YS5mcmFtZShkcm93bj1kcm93bjJbMjpsZW5ndGgoZHJvd24yKV0sDQogICAgICAgICAgICAgICAgIGljZWNyZWFtPWljWzEgOiBsZW5ndGgoZHJvd24yKS0xXSwNCiAgICAgICAgICAgICAgICAgaWNlY3JlYW1wcmljZT1pY3ByaWNlWzExOihsZW5ndGgoZHJvd24yKSs5KV0sDQogICAgICAgICAgICAgICAgIHllYXI9MjAwMDoyMDA5KQ0KZGYkZHJvd24gPC0gZGYkZHJvd24gLyBtZWFuKGRmJGRyb3duKQ0KZGYkaWNlY3JlYW0gPC0gZGYkaWNlY3JlYW0gLyBtZWFuKGRmJGljZWNyZWFtKQ0KZGYkaWNlY3JlYW0yIDwtIGRmJGljZWNyZWFtKmRmJGljZWNyZWFtcHJpY2UgLyBtZWFuKGRmJGljZWNyZWFtKmRmJGljZWNyZWFtcHJpY2UpDQpkZiRpY2VjcmVhbTMgPC0gZGYkaWNlY3JlYW1wcmljZSAvIG1lYW4oZGYkaWNlY3JlYW1wcmljZSkNCmNvcl91bml0IDwtIGNvcih4PWRmJGljZWNyZWFtLCB5PWRmJGRyb3duKQ0KY29yX3JldiA8LSBjb3IoeD1kZiRpY2VjcmVhbTIsIHk9ZGYkZHJvd24pDQpjb3JfcHJpY2UgPC0gY29yKHg9ZGYkaWNlY3JlYW1wcmljZSwgeT1kZiRkcm93bikNCmxpYnJhcnkoZ2dwbG90MikNCg0KbGlicmFyeShyZXNoYXBlMikNCmRtIDwtIG1lbHQoZGZbYygiZHJvd24iLCAiaWNlY3JlYW0yIiwgInllYXIiKV0saWQudmFyPTMpDQpwbG90MSA8LSBnZ3Bsb3QoZG0sIGFlcyh4PXllYXIsIHk9dmFsdWUsIGNvbG91cj12YXJpYWJsZSkpICsgDQogIGdlb21fcG9pbnQoc2hhcGU9MSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9bG9lc3MsIHNlPUYpICsNCiAgbGFicyh4PSJZZWFyIiwNCiAgICAgICB5PSIlIG9mIGF2ZXJhZ2UgeWVhciIpICsgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygyMDAwLDIwMDMsMjAwNiwyMDA5KSkgKyANCiAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWU9IlN0YXRpc3RpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICBicmVha3M9YygiaWNlY3JlYW0yIiwiZHJvd24iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJJY2UgY3JlYW0gcmV2ZW51ZSIsIkRyb3duaW5nIGluIHBvb2xzIikpDQpkbSA8LSBtZWx0KGRmW2MoImRyb3duIiwgImljZWNyZWFtMyIsICJ5ZWFyIildLGlkLnZhcj0zKQ0KcGxvdDIgPC0gZ2dwbG90KGRtLCBhZXMoeD15ZWFyLCB5PXZhbHVlLCBjb2xvdXI9dmFyaWFibGUpKSArIA0KICBnZW9tX3BvaW50KHNoYXBlPTEpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxvZXNzLCBzZT1GKSArDQogIGxhYnMoeD0iWWVhciIsDQogICAgICAgeT0iJSBvZiBhdmVyYWdlIHllYXIiKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMjAwMCwyMDAzLDIwMDYsMjAwOSkpICsgDQogIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShuYW1lPSJTdGF0aXN0aWMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPWMoImljZWNyZWFtMyIsImRyb3duIiksDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiSWNlIGNyZWFtIHZvbHVtZSBzb2xkIiwiRHJvd25pbmcgaW4gcG9vbHMiKSkNCmRtIDwtIG1lbHQoZGZbYygiZHJvd24iLCAiaWNlY3JlYW0iLCAieWVhciIpXSxpZC52YXI9MykNCnBsb3QzIDwtIGdncGxvdChkbSwgYWVzKHg9eWVhciwgeT12YWx1ZSwgY29sb3VyPXZhcmlhYmxlKSkgKyANCiAgZ2VvbV9wb2ludChzaGFwZT0xKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD1sb2Vzcywgc2U9RikgKw0KICBsYWJzKHg9IlllYXIiLA0KICAgICAgIHk9IiUgb2YgYXZlcmFnZSB5ZWFyIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDIwMDAsMjAwMywyMDA2LDIwMDkpKSArIA0KICBzY2FsZV9jb2xvdXJfZGlzY3JldGUobmFtZT0iU3RhdGlzdGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKCJpY2VjcmVhbSIsImRyb3duIiksDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiSWNlIGNyZWFtIHByaWNlIiwiRHJvd25pbmcgaW4gcG9vbHMiKSkNCmBgYA0KDQpgYGB7ciBncmFwaDNfcGFydDIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA0LjN9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShwbG90bHkpKQ0KZ2dwbG90bHkocGxvdDEpDQpgYGANCg0KYGBge3IgZ3JhcGgzX3BhcnQ0LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gNC4zfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkNCmdncGxvdGx5KHBsb3QzKQ0KYGBgDQoNCmBgYHtyIGdyYXBoM19wYXJ0Mywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDQuM30NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpDQpnZ3Bsb3RseShwbG90MikNCmBgYA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBBZGRpdGlvbiB1c2VzICcrJw0KMSArIDENCg0KIyBTdWJ0cmFjdGlvbiB1c2VzICctJw0KMiAtIDENCg0KIyBNdWx0aXBsaWNhdGlvbiB1c2VzICcqJw0KMyAqIDMNCg0KIyBEaXZpc2lvbiB1c2VzICcvJw0KNCAvIDINCmBgYA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBFeHBvbmVudGlhdGlvbiB1c2VzICdeJw0KNSBeIDUNCg0KIyBNb2R1bHVzIChha2EgdGhlIHJlbWFpbmRlcikgdXNlcyAnJSUnDQo0NiAlJSA2DQoNCiMgSW50ZWdlciBkaXZpc2lvbiB1c2VzICclLyUnDQo0NiAlLyUgNg0KDQpgYGANCg0KYGBge3IgZXZhbD1UUlVFfQ0KIyBTdG9yZSAyIGluICd4Jw0KeCA8LSAyDQoNCiMgQ2hlY2sgdGhlIHZhbHVlIG9mIHgNCngNCg0KIyBTdG9yZSBhcml0aG1ldGljIGluIHkNCnkgPC0geCAqIDINCg0KIyBDaGVjayB0aGUgdmFsdWUgb2YgeQ0KeQ0KYGBgDQoNCmBgYHtyLCBldmFsPVRSVUV9DQojIFByZXZpb3VzIHZhbHVlIG9mIHggYW5kIHkNCnBhc3RlKHgsIHkpDQoNCiMgQ2hhbmdlIHgsIHRoZW4gcmVjaGVjayB0aGUgdmFsdWUNCiMgb2YgeCBhbmQgeQ0KeCA8LSAyMDANCg0KcGFzdGUoeCwgeSkNCmBgYA0KDQpgYGB7ciBTaW5ndGVsLCBldmFsPVRSVUV9DQojIERhdGEgZnJvbSBTaW5ndGVsJ3MgZWFybmluZ3MgcmVwb3J0cywgaW4gTWlsbGlvbnMgb2YgU0dEDQpzaW5ndGVsXzIwMTcgPC0gMzgzMS4wDQpzaW5ndGVsXzIwMTggPC0gNTQzMC4zDQoNCiMgQ29tcHV0ZSBncm93dGgNCmdyb3d0aCA8LSBzaW5ndGVsXzIwMTggLyBzaW5ndGVsXzIwMTcgLSAxDQoNCiMgQ2hlY2sgdGhlIHZhbHVlIG9mIGdyb3d0aA0KZ3Jvd3RoDQpgYGANCg0KYGBge3IsIGV2YWw9VFJVRX0NCmNvbXBhbnlfbmFtZSA8LSAiR29vZ2xlIiAgIyBjaGFyYWN0ZXINCmNvbXBhbnlfbmFtZQ0KDQpjb21wYW55X25hbWUgPC0gJ0dvb2dsZScgICMgY2hhcmFjdGVyDQpjb21wYW55X25hbWUNCg0KdGVjaF9maXJtIDwtIFRSVUUgICMgYm9vbGVhbg0KdGVjaF9maXJtDQoNCmVhcm5pbmdzIDwtIDEyNjYyICAjIG51bWVyaWMsICRNIFVTRA0KZWFybmluZ3MNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KZGYgPC0gcmVhZC5jc3YoIi4uLy4uL0RhdGEvU2Vzc2lvbl8xLTIuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkNCiMgRmlsdGVyIHRvIGZpcm1zIHdpdGggYXQgbGVhc3QgJDFNIFVTRCByZXZlbnVlLCBrbm93biBuZXQgaW5jb21lLCBhbmQgZmlzY2FsIHllYXIgb2YgMjAxNw0KY2xlYW5fZGYgPC0gZGYgJT4lIGZpbHRlcihmeWVhcj09MjAxNywgIWlzLm5hKHJldnQpLCAhaXMubmEobmkpLCByZXZ0ID4gMSkNCnRlY2hfZGYgPC0gY2xlYW5fZGYgJT4lDQogIGZpbHRlcihnc2VjdG9yPT00NSkgJT4lDQogIG11dGF0ZShyZXZlbnVlID0gcmV2dCwNCiAgICAgICAgIGVhcm5pbmdzID0gbmksDQogICAgICAgICBtYXJnaW4gPSBuaS9yZXZ0KQ0KZWFybmluZ3NfMjAxNyA8LSB0ZWNoX2RmJG5pDQpyZXZlbnVlXzIwMTcgPC0gdGVjaF9kZiRyZXZ0DQpuYW1lc18yMDE3IDwtIHRlY2hfZGYkY29ubQ0KbmFtZXMoZWFybmluZ3NfMjAxNykgPC0gbmFtZXNfMjAxNw0KbmFtZXMocmV2ZW51ZV8yMDE3KSA8LSBuYW1lc18yMDE3DQpgYGANCg0KYGBge3IsIGV2YWw9VFJVRX0NCiMgQ2FsY3VsYXRpbmcgcHJvaXQgbWFyZ2luIGZvciBhbGwgcHVibGljIFVTIHRlY2ggZmlybXMNCiMgNzE1IHRlY2ggZmlybXMgaW4gQ29tcHVzdGF0IHdpdGggPjFNIHNhbGVzIGluIDIwMTcNCg0KIyBEYXRhOg0KIyAgICBlYXJuaW5nc18yMDE3OiB2ZWN0b3Igb2YgZWFybmluZ3MsICRNIFVTRA0KIyAgICByZXZlbnVlXzIwMTc6IHZlY3RvciBvZiByZXZlbnVlLCAkTSBVU0QNCiMgICAgbmFtZXNfMjAxNzogYSB2ZWN0b3Igb2YgdGlja2VycyAoc3RyaW5ncykNCg0KIyBOYW1pbmluZyB0aGUgdmVjdG9ycw0KbmFtZXMoZWFybmluZ3NfMjAxNykgPC0gbmFtZXNfMjAxNw0KbmFtZXMocmV2ZW51ZV8yMDE3KSA8LSBuYW1lc18yMDE3DQoNCmVhcm5pbmdzXzIwMTdbMTo2XQ0KcmV2ZW51ZV8yMDE3WzE6Nl0NCmBgYA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBTdW1tYXJpemluZyB2ZWN0b3JzDQpzdW1tYXJ5KGVhcm5pbmdzXzIwMTcpDQpgYGANCg0KYGBge3IsIGV2YWw9VFJVRX0NCnN1bW1hcnkocmV2ZW51ZV8yMDE3KQ0KYGBgDQoNCmBgYHtyLCBldmFsPVRSVUV9DQojIENhbGN1bGF0aW5nIHByb2ZpdCBtYXJnaW4NCm1hcmdpbiA8LSBlYXJuaW5nc18yMDE3IC8gcmV2ZW51ZV8yMDE3DQpzdW1tYXJ5KG1hcmdpbikNCmBgYA0KDQpgYGB7ciwgZXZhbD1UUlVFfQ0KIyBXb3JzdCwgbWlkcG9pbnQsIGFuZCBiZXN0IHByb2ZpdCBtYXJnaW4gZmlybXMgaW4gMjAxNy4gT3VyIG5hbWVzIGNhcnJpZWQgb3ZlciA6KQ0KbWFyZ2luW29yZGVyKG1hcmdpbildW2MoMSxsZW5ndGgobWFyZ2luKS8yLGxlbmd0aChtYXJnaW4pKV0NCmBgYA0KDQpgYGB7cn0NCmNvbHVtbnMgPC0gYygiR29vZ2xlIiwgIk1pY3Jvc29mdCIsDQogICAgICAgICAgICAgIkdvbGRtYW4iKQ0Kcm93cyA8LSBjKCJFYXJuaW5ncyIsIlJldmVudWUiKQ0KDQpmaXJtX2RhdGEgPC0gbWF0cml4KGRhdGE9DQogIGMoMTI2NjIsIDIxMjA0LCA0Mjg2LCAxMTA4NTUsDQogICAgODk5NTAsIDQyMjU0KSwgbnJvdz0yKQ0KIyBFcXVpdmFsZW50Og0KIyBtYXRyaXgoZGF0YT1jKDEyNjYyLCAyMTIwNCwgNDI4NiwNCiMgICAxMTA4NTUsIDg5OTUwLCA0MjI1NCksIG5jb2w9MykNCg0KIyBBcHBseSBuYW1lcw0Kcm93bmFtZXMoZmlybV9kYXRhKSA8LSByb3dzDQpjb2xuYW1lcyhmaXJtX2RhdGEpIDwtIGNvbHVtbnMNCg0KIyBQcmludCB0aGUgbWF0cml4DQpmaXJtX2RhdGENCmBgYA0KDQpgYGB7cn0NCmZpcm1fZGF0YVsyLCAzXQ0KZmlybV9kYXRhWywgYygiR29vZ2xlIiwgIk1pY3Jvc29mdCIpXQ0KZmlybV9kYXRhWzEsXQ0KZmlybV9kYXRhWyJSZXZlbnVlIiwgIkdvbGRtYW4iXQ0KYGBgDQoNCmBgYHtyfQ0KaW5kY29kZSA8LSBjKDQ1LDQ1LDQwKQ0KanBkYXRhIDwtIGMoMTczNzAsIDExNTQ3NSkNCiMgUHJlbG9hZGVkOiBpbmR1c3RyeSBjb2RlcyBhcyBpbmRjb2RlICh2ZWN0b3IpDQpgYGANCg0KYGBge3J9DQojIFByZWxvYWRlZDogaW5kdXN0cnkgY29kZXMgYXMgaW5kY29kZSAodmVjdG9yKQ0KIyAgICAgLSBHSUNTIGNvZGVzOiA0MD1GaW5hbmNpYWxzLCA0NT1JbmZvcm1hdGlvbiBUZWNobm9sb2d5DQojICAgICAtIFNlZTogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2xvYmFsX0luZHVzdHJ5X0NsYXNzaWZpY2F0aW9uX1N0YW5kYXJkDQojIFByZWxvYWRlZDogSlBNb3JnYW4gZGF0YSBhcyBqcGRhdGEgKHZlY3RvcikNCg0KbWF0IDwtIHJiaW5kKGZpcm1fZGF0YSxpbmRjb2RlKSAgIyBBZGQgYSByb3cNCnJvd25hbWVzKG1hdClbM10gPC0gIkluZHVzdHJ5IiAgIyBOYW1lIHRoZSBuZXcgcm93DQptYXQNCm1hdCA8LSBjYmluZChmaXJtX2RhdGEsanBkYXRhKSAgIyBBZGQgYSBjb2x1bW4NCmNvbG5hbWVzKG1hdClbNF0gPC0gIkpQTW9yZ2FuIiAgIyBOYW1lIHRoZSBuZXcgY29sdW1uDQptYXQNCmBgYA0KDQpgYGB7cn0NCiMgSWdub3JlIHRoaXMgY29kZSBmb3Igbm93Li4uDQptb2RlbCA8LSBzdW1tYXJ5KGxtKGVhcm5pbmdzIH4gcmV2ZW51ZSwgZGF0YT10ZWNoX2RmKSkNCiNOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBpcyBoaWRpbmcgc29tZXRoaW5nLi4uDQptb2RlbA0KYGBgDQoNCmBgYHtyfQ0KbW9kZWxbInIuc3F1YXJlZCJdDQptb2RlbFtbInIuc3F1YXJlZCJdXQ0KbW9kZWwkci5zcXVhcmVkDQpgYGANCg0KYGBge3J9DQplYXJuaW5ncyA8LSBjKDEyNjYyLCAyMTIwNCwgNDI4NikNCmNvbXBhbnkgPC0gYygiR29vZ2xlIiwgIk1pY3Jvc29mdCIsICJHb2xkbWFuIikNCm5hbWVzKGVhcm5pbmdzKSA8LSBjb21wYW55DQplYXJuaW5nc1siR29vZ2xlIl0NCmVhcm5pbmdzW1siR29vZ2xlIl1dDQojQ2FuJ3QgdXNlICQgd2l0aCB2ZWN0b3JzDQpgYGANCg0KYGBge3J9DQpzdHIobW9kZWwpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICMgSW1wb3J0cyBtb3N0IHRpZHkgcGFja2FnZXMNCiMgQmFzZSBSIGRhdGEgaW1wb3J0IC0tIHN0cmluZ3NBc0ZhY3RvcnMgaXMgaW1wb3J0YW50IGhlcmUNCmRmIDwtIHJlYWQuY3N2KCIuLi8uLi9EYXRhL1Nlc3Npb25fMS0yLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpDQpkZiA8LSBzdWJzZXQoZGYsIGZ5ZWFyID09IDIwMTcgJiAhaXMubmEocmV2dCkgJiAhaXMubmEobmkpICYNCiAgICAgICAgICAgICByZXZ0ID4gMSAmIGdzZWN0b3IgPT0gNDUpDQpkZiRtYXJnaW4gPSBkZiRuaSAvIGRmJHJldnQNCnN1bW1hcnkoZGYpDQpgYGANCg0KYGBge3IsIG1lc3NhZ2U9Rn0NCiMgVGlkeSBpbXBvcnQNCmRmIDwtIHJlYWRfY3N2KCIuLi8uLi9EYXRhL1Nlc3Npb25fMS0yLmNzdiIpICU+JQ0KICBmaWx0ZXIoZnllYXIgPT0gMjAxNywgICAgICAjIGZpc2NhbCB5ZWFyDQogICAgICAgICAhaXMubmEocmV2dCksICAgICAgICMgcmV2ZW51ZSBub3QgbWlzc2luZw0KICAgICAgICAgIWlzLm5hKG5pKSwgICAgICAgICAjIG5ldCBpbmNvbWUgbm90IG1pc3NpbmcNCiAgICAgICAgIHJldnQgPiAxLCAgICAgICAgICAgIyBhdCBsZWFzdCAxTSBVU0QgaW4gcmV2ZW51ZQ0KICAgICAgICAgZ3NlY3RvciA9PSA0NSkgJT4lICAjIHRlY2ggZmlybQ0KICBtdXRhdGUobWFyZ2luID0gbmkvcmV2dCkgICAjIHByb2ZpdCBtYXJnaW4NCnN1bW1hcnkoZGYpDQpgYGANCg0KYGBge3IsIG1lc3NhZ2U9Rn0NCmRmIDwtIG11dGF0ZSgNCiAgICAgICAgZmlsdGVyKA0KICAgICAgICAgIHJlYWRfY3N2KCIuLi8uLi9EYXRhL1Nlc3Npb25fMS0yLmNzdiIpLA0KICAgICAgICAgIGZ5ZWFyID09IDIwMTcsICAgICAgIyBmaXNjYWwgeWVhcg0KICAgICAgICAgICFpcy5uYShyZXZ0KSwgICAgICAgIyByZXZlbnVlIG5vdCBtaXNzaW5nDQogICAgICAgICAgIWlzLm5hKG5pKSwgICAgICAgICAjIG5ldCBpbmNvbWUgbm90IG1pc3NpbmcNCiAgICAgICAgICByZXZ0ID4gMSwgICAgICAgICAgICMgYXQgbGVhc3QgMU0gVVNEIGluIHJldmVudWUNCiAgICAgICAgICBnc2VjdG9yID09IDQ1KSwgICMgdGVjaCBmaXJtDQogICAgICAgIG1hcmdpbiA9IG5pL3JldnQpICAgIyBwcm9maXQgbWFyZ2luDQpzdW1tYXJ5KGRmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUbyBzZWUgdGhlIGFyZ3VtZW50cyBhIGZ1bmN0aW9uIHRha2VzLCBydW46DQphcmdzKGRhdGEuZnJhbWUpDQpgYGANCg0KYGBge3J9DQphZGRfdHdvIDwtIGZ1bmN0aW9uKG4pIHsNCiAgbiArIDINCn0NCg0KYWRkX3R3byg1MDApDQpgYGANCg0KYGBge3J9DQptdWx0X3RvZ2V0aGVyIDwtIGZ1bmN0aW9uKG4xLCBuMj0wLCBzcXVhcmU9RkFMU0UpIHsNCiAgaWYgKCFzcXVhcmUpIHsNCiAgICBuMSAqIG4yDQogIH0gZWxzZSB7DQogICAgbjEgKiBuMQ0KICB9DQp9DQoNCm11bHRfdG9nZXRoZXIoNSw2KQ0KbXVsdF90b2dldGhlcig1LDYsc3F1YXJlPVRSVUUpDQptdWx0X3RvZ2V0aGVyKDUsc3F1YXJlPVRSVUUpDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQ0KRlhSYXRlIDwtIGZ1bmN0aW9uKGZyb209IlVTRCIsIHRvPSJTR0QiLCBkdD1TeXMuRGF0ZSgpKSB7DQogIG9wdGlvbnMoImdldFN5bWJvbHMud2FybmluZzQuMCI9RkFMU0UpDQogIHJlcXVpcmUocXVhbnRtb2QpDQogIGRhdGEgPC0gZ2V0U3ltYm9scyhwYXN0ZTAoZnJvbSwgIi8iLCB0byksIGZyb209ZHQtMywgdG89ZHQsIHNyYz0ib2FuZGEiLCBhdXRvLmFzc2lnbj1GKQ0KICByZXR1cm4oZGF0YVtbMV1dKQ0KfQ0KZGF0ZSgpDQpGWFJhdGUoZnJvbT0iVVNEIiwgdG89IlNHRCIpICAjIFRvZGF5J3MgU0dEIHRvIFVTRCByYXRlDQpGWFJhdGUoZnJvbT0iU0dEIiwgdG89IkNOWSIpICAjIFRvZGF5J3MgU0dEIHRvIENOWSByYXRlDQpGWFJhdGUoZnJvbT0iVVNEIiwgdG89IlNHRCIsIGR0PVN5cy5EYXRlKCktOTApICAjIExhc3QgcXVhcnRlcidzIFNHRCB0byBVU0QgcmF0ZQ0KYGBgDQoNCg==