drawing

Seasons 1950-2017





Setup Stage


Loading necessary packages.

library(dplyr)
library(tidyr)
library(ggplot2)
library(RColorBrewer)
library(plotly)
library(ggridges)
library(kableExtra)
library(wordcloud2)
library(forcats)


Create additional functions

# Mode average
getmode <- function(v) {
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}


Load the dataset


Dataset taken from: https://www.kaggle.com/drgilermo/nba-players-stats/version/2

If you need to see the glossary and the data tidying process, please visit first part here.

Loading datasets…

NBA <- read.csv("NBA_TidySet.csv")
NBA_Scaled <- read.csv("NBA_Scaled_TidySet.csv")
NBA$Pos <- factor(NBA$Pos, levels = c("C", "PF", "SF", "SG", "PG"))
PosColorCode <- c("C"="#FF0000", "PF"="#FFA500", "SF"="#DDDD00" ,"SG"="#0000FF", "PG"="#32CD32")


Displaying main table

NBA





SHOOTING


In this session we explore shooting statistics of the NBA players. As discussed in the previous session, there are 3 basic shooting category in basketball: Field Goals (FG), Three-Points Field Goal (3P), and Free-Throw (FT). Last session we already see the exploratory analysis of FG and FT, now it’s time too see 3-Points stats with additional mixed shooting stats.

Again, I use basic RGB as color-code for shooting type: red for FT, green for 3P and blue for FG.

NOTE: While I don’t have time or an inspiration about the narrative, Lorem ipsum texts will be in display.



Three-Points


Since three-points is my favorite category in a basketball statistics, I wil selfishly dig deeper than any other shooting category. So here we go.


Annual 3-Points % League Leaders


No qui autem reque. Ea tantas vocent dolores vim, an vivendum deserunt sensibus vix. Ea mel zril aliquam appellantur, utinam omnesque liberavisse per ea. Consul ridens voluptatum ea per, laoreet nominati salutandi ne his. Assum graecis voluptatibus id sit, cum id constituto adipiscing.

Top3Pts. <- NBA %>%
    filter(Year >= 1980 & X3P. > 0) %>%
    group_by(Year, Player) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              Games = sum(G),
              MPG = round(sum(MP)/Games, 1),
              PPG = round(sum(PTS)/Games, 1),
              x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3Pt. = round(x3Pm/x3Pa, 3)) %>%
    filter(ifelse(Year %in% 1980:1990, x3Pm >= 25,
                  ifelse(Year %in% 1991:1994, x3Pm >= 50,
                    ifelse(Year %in% c(1998, 2000, 2004:2008, 2010), x3Pm >= 55,
                      ifelse(Year %in% 1999, x3Pm >= 34,
                        ifelse(Year %in% 2012, x3Pm >= 44,
                          x3Pm >= 82)))))) %>%
    group_by(Year) %>%
    summarise(Player = Player[which.max(x3Pt.)],
              Team = Team[which.max(x3Pt.)],
              Pos = Pos[which.max(x3Pt.)],
              Games = Games[which.max(x3Pt.)],
              MPG = MPG[which.max(x3Pt.)],
              PPG = PPG[which.max(x3Pt.)],
              x3Pm = x3Pm[which.max(x3Pt.)],
              x3Pa = x3Pa[which.max(x3Pt.)],
              x3Pt. = max(x3Pt., na.rm = T)) %>%
    arrange(desc(Year))
Top3Pts. %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "Annual 3-Points % League Leaders") %>%
    kable_styling("striped", full_width = T) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(2, bold = T) %>%
    column_spec(10, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
Annual 3-Points % League Leaders
Year Player Team Pos Games MPG PPG x3Pm x3Pa x3Pt.
2017 Kyle Korver ATL SG 67 26.2 10.1 162 359 0.451
2016 J.J. Redick LAC SG 75 28.0 16.3 200 421 0.475
2015 Kyle Korver ATL SG 75 32.2 12.1 221 449 0.492
2014 Kyle Korver ATL SG 71 33.9 12.0 185 392 0.472
2013 Jose Calderon TOR PG 73 29.6 11.3 130 282 0.461
2012 Steve Novak NYK PF 54 18.9 8.8 133 282 0.472
2011 Matt Bonner SAS PF 66 21.7 7.3 105 230 0.457
2010 Kyle Korver UTA SG 52 18.3 7.2 59 110 0.536
2009 Anthony Morrow GSW SG 67 22.6 10.1 86 184 0.467
2008 Jason Kapono TOR SF 81 18.9 7.2 57 118 0.483
2007 Jason Kapono MIA SF 67 26.4 10.9 108 210 0.514
2006 Richard Hamilton DET SG 80 35.3 20.1 55 120 0.458
2005 Fred Hoiberg MIN SG 76 16.7 5.8 70 145 0.483
2004 Anthony Peeler SAC SG 75 18.5 5.7 68 141 0.482
2003 Bruce Bowen SAS SF 82 31.3 7.1 101 229 0.441
2002 Steve Smith SAS SG 77 28.7 11.6 116 246 0.472
2001 Brent Barry SEA SG 67 26.5 8.8 109 229 0.476
2000 Hubert Davis DAL PG 79 23.0 7.4 82 167 0.491
1999 Dell Curry MIL SG 42 20.6 10.1 69 145 0.476
1998 Dale Ellis SEA SF 79 24.5 11.8 127 274 0.464
1997 Glen Rice CHH SF 79 42.6 26.8 207 440 0.470
1996 Tim Legler WSB SG 77 23.1 9.4 128 245 0.522
1995 Steve Kerr CHI PG 82 22.4 8.2 89 170 0.524
1994 Tracy Murray POR SF 66 12.4 6.6 50 109 0.459
1993 B.J. Armstrong CHI PG 82 30.4 12.3 63 139 0.453
1992 Dana Barros SEA PG 75 17.7 8.3 83 186 0.446
1991 Jim Les SAC PG 55 25.4 7.2 71 154 0.461
1990 Steve Kerr CLE PG 78 21.3 6.7 73 144 0.507
1989 Jon Sundvold MIA SG 68 19.7 10.4 48 92 0.522
1988 Craig Hodges MIL SG 66 21.9 9.5 86 175 0.491
1987 Kiki Vandeweghe POR SF 79 38.3 26.9 39 81 0.481
1986 Craig Hodges MIL SG 66 26.3 10.8 73 162 0.451
1985 Byron Scott LAL SG 81 28.5 16.0 26 60 0.433
1984 Darrell Griffith UTA SG 82 32.3 20.0 91 252 0.361
1983 Mike Dunleavy SAS PG 79 20.5 7.8 67 194 0.345
1982 Campy Russell NYK SF 77 30.6 13.9 25 57 0.439
1981 Brian Taylor SDC PG 80 28.9 10.1 44 115 0.383
1980 Fred Brown SEA SG 80 21.3 12.0 39 88 0.443

To qualify for league leaders, a player must meet the Rate Statistic Requirements


Vix doming conceptam percipitur cu, qui essent vidisse nusquam et! Vivendo voluptaria scribentur sit ut. Vis modus ceteros contentiones at, id regione moderatius sea? Te vim natum hendrerit.

mean3PT.Year <- NBA %>%
    group_by(Year) %>%
    filter(Year >= 1980 & X3P. > 0) %>%
    summarise(x3PT. = sum(X3P, na.rm = T)/sum(X3PA, na.rm = T))
T3p. <- Top3Pts. %>%
    ggplot() +
    geom_bar(aes(Year, x3Pt., fill = Pos, text = paste("Player:", Player)), stat = "identity") +
    geom_line(data=mean3PT.Year, aes(Year, x3PT., linetype = "Average line"), color = "black") +
    ggtitle("Annual 3-point Shooting Leaders") +
    geom_hline(yintercept = mean(Top3Pts.$x3Pt.), col = "blue", alpha = 0.5) +
    scale_x_continuous(breaks = seq(1950, 2017, 10)) +
    scale_fill_manual("Pos", values = PosColorCode) +
    theme(legend.position="none")
pT3p. <- ggplotly(T3p.)
#api_create(pT3p., filename = "Annual3P% Leaders")
Annual3P% Leaders


  • Average top 3P% is 0.467.
  • Highest top 3P% is 0.536, in season 2010.
  • Lowest top 3P% is 0.345, in season 1983.
  • The SG dominated the league by 50%, followed by PG with 23.7%, then SF
    added with 21.1%, and finally PF completed the leaderboard with only 5.3%. No center ever be in 3P% leaderboard, this because 3-Points is requires more long-range shooting skills which not the role for centers.





All-Time 3 Points Field Goals % Leaders


Sed turpis est, pretium nec elit lacinia, accumsan tristique enim. Curabitur consectetur risus sit amet mauris vestibulum rhoncus. Nam non finibus augue, nec rhoncus nulla. Nam in molestie ipsum. Praesent pharetra fermentum orci, et faucibus odio porta non. Duis finibus mattis elementum. Mauris posuere dolor nec aliquam mattis. Etiam elementum erat ex, at dapibus lacus convallis aliquam. Nam eget magna lacus.

AllTime3P. <- NBA %>%
    filter(X3P. > 0) %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              ActiveYears = paste(getmode(YearStart), "-", getmode(YearEnd)),
              Games = sum(G),
              MPG = round(sum(MP) / Games, 1),
              PPG = round(sum(PTS) / Games, 1),
              x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3P. = round(x3Pm/x3Pa, 3)) %>%
    filter(x3Pm >= 250) %>%
    arrange(desc(x3P.), desc(x3Pm)) %>%
    mutate(Rank = min_rank(desc(x3P.))) %>%
    select(Rank, everything())
AllTime3P. %>%
    head(n=10) %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "All-Time 3 Points Field Goals % Leaders") %>%
    kable_styling("striped", full_width = T) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(2, bold = T) %>%
    column_spec(11, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
All-Time 3 Points Field Goals % Leaders
Rank Player Pos Team ActiveYears Games MPG PPG x3Pm x3Pa x3P.
1 Steve Kerr PG CHI 1989 - 2003 905 17.9 6.0 726 1597 0.455
2 Hubert Davis SG NYK 1993 - 2004 668 22.6 8.4 728 1650 0.441
3 Stephen Curry PG GSW 2010 - 2018 574 34.7 22.8 1917 4379 0.438
4 Drazen Petrovic SG NJN 1990 - 1993 290 26.4 15.4 255 583 0.437
5 Tim Legler SG DAL 1990 - 2000 288 17.7 7.4 260 596 0.436
6 Jason Kapono SF MIA 2004 - 2012 509 17.8 6.7 457 1054 0.434
7 Kyle Korver SG PHI 2004 - 2018 1031 26.5 10.0 2049 4754 0.431
8 Steve Novak SF HOU 2007 - 2017 467 12.1 4.7 575 1337 0.430
9 Steve Nash PG PHO 1997 - 2014 1217 31.3 14.3 1685 3939 0.428
10 B.J. Armstrong PG CHI 1990 - 2000 743 23.9 9.8 436 1025 0.425


Let’s go to the plot.

LabelFix <- AllTime3P. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(x3P.), fun=mean),
           x3Pa = x3Pa-x3Pm,
           x3P. = paste(x3P. * 100, "%")) %>%
    head(n=10) %>%
    gather(Parameter, Count, x3Pm:x3Pa, -c(Player, x3P.)) %>%
    filter(Parameter == "x3Pa")
    
AllTime3P. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(x3P.), fun=mean),
           x3Pa = x3Pa-x3Pm) %>%
    gather(Parameter, Count, x3Pm:x3Pa, -c(Player, x3P.)) %>%
    ggplot(aes(Player, Count, fill = Parameter)) + 
    geom_bar(stat = 'identity', position = 'stack') +
    geom_label(aes(label = x3P.,
                   y = 5000),
               data = LabelFix,
               stat = 'identity',
               position = "stack",
               fill = "#FF4000",
               color = "#00FFFF",
               size = 4.5) +
    ggtitle("All-Time 3 Points Percentage Leaders") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    scale_fill_manual(labels=c("3Pts-missed  ", "3Pts-made"), values=c("#66B266", "#008000")) +
    theme(legend.title=element_blank()) +
    theme(legend.position="bottom")


And again, here comes my favorite boxes, the result of combining HTML, CSS and R code.

Top3P.Pos <- AllTime3P. %>%
    group_by(Pos) %>%
    summarise(Player = Player[which.max(x3P.)],
              Team = Team[which.max(x3P.)],
              ActiveYears = ActiveYears[which.max(x3P.)],
              Games = Games[which.max(x3P.)],
              MPG = MPG[which.max(x3P.)],
              PPG = PPG[which.max(x3P.)],
              x3Pm = x3Pm[which.max(x3P.)],
              x3Pa = x3Pa[which.max(x3P.)],
              x3P. = x3P.[which.max(x3P.)])

Best 3-Points % by Position

C


Mehmet Okur

0.375
  • FG: 596 / 1590
  • PPG: 13.5
  • Years: 2003 - 2012

PF


Matt Bonner

0.414
  • FG: 797 / 1923
  • PPG: 5.8
  • Years: 2005 - 2016

SF


Jason Kapono

0.434
  • FG: 457 / 1054
  • PPG: 6.7
  • Years: 2004 - 2012

SG


Hubert Davis

0.441
  • FG: 728 / 1650
  • PPG: 8.4
  • Years: 1993 - 2004

PG


Steve Kerr

0.455
  • FG: 726 / 1597
  • PPG: 6
  • Years: 1989 - 2003



3-Points per 36 Minutes


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nec pulvinar eros. Proin elementum nunc eget fringilla eleifend. Nulla facilisi. Vestibulum vitae lacus et felis facilisis pharetra. In id diam lacus. Fusce et pulvinar urna, ac gravida ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus tristique viverra suscipit. Suspendisse eleifend mollis euismod. Aliquam sed turpis ipsum. Donec et libero sit amet mi ultricies porta.

AllTime3Pma <- NBA %>%
    group_by(Player) %>%
    filter(sum(G) >= 400) %>%
    summarise(Pos = getmode(Position),
              Team = getmode(Tm),
              ActiveYears = paste(getmode(YearStart), "-", getmode(YearEnd)),
              Games = sum(G),
              Minutes = sum(MP),
              MPG = round(Minutes /sum(G), 1),
              PPG = round(sum(PTS)/sum(G), 1),
              x3Pm_36 = round((sum(X3P) / Minutes) * 36, 1),
              x3Pm_G = round(sum(X3P) / Games, 1),
              x3Pa_36 = round((sum(X3PA) / Minutes) * 36, 1),
              x3Pa_G = round(sum(X3PA) / Games, 1),
              x3P. = round(sum(X3P)/sum(X3PA), 3)) %>%
    select(-"Minutes")
AllTime3Pma %>%
    arrange(desc(x3Pm_36), desc(x3P.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(x3Pm_36))) %>%
    select(Rank, everything()) %>%
    kable(escape = FALSE, align = "c", caption = "3-Points Made per 36 Minutes") %>%
    kable_styling("striped", full_width = T, font_size = 11) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(c(2, 13), bold = T) %>%
    column_spec(9, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
3-Points Made per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG x3Pm_36 x3Pm_G x3Pa_36 x3Pa_G x3P.
1 Steve Novak SF HOU 2007 - 2017 467 12.1 4.7 3.7 1.2 8.5 2.9 0.430
2 Stephen Curry PG GSW 2010 - 2018 574 34.7 22.8 3.5 3.3 7.9 7.6 0.438
3 Klay Thompson SG GSW 2012 - 2018 464 32.7 19.1 3.1 2.9 7.5 6.8 0.419
4 Ryan Anderson PF ORL 2009 - 2018 556 26.5 13.2 2.9 2.1 7.6 5.6 0.381
5 Patty Mills PG SAS 2010 - 2018 441 17.0 7.9 2.8 1.3 7.2 3.4 0.395
6 Kyle Korver SG PHI 2004 - 2018 1031 26.5 10.0 2.7 2.0 6.3 4.6 0.431
6 J.J. Redick SG ORL 2007 - 2018 690 24.9 11.9 2.7 1.8 6.4 4.4 0.415
6 J.R. Smith SG DEN 2005 - 2018 880 27.0 12.9 2.7 2.0 7.2 5.4 0.374
9 Danny Green SG SAS 2010 - 2018 470 25.0 8.8 2.6 1.8 6.5 4.5 0.400
10 James Jones SF MIA 2004 - 2017 709 15.7 5.2 2.5 1.1 6.3 2.7 0.401


AllTime3Pma %>%
    arrange(desc(x3Pa_36), desc(x3P.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(x3Pa_36))) %>%
    select(Rank, everything()) %>%
    kable(escape = FALSE, align = "c", caption = "3-Points Attempts per 36 Minutes") %>%
    kable_styling("striped", full_width = T, font_size = 11) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(c(2, 13), bold = T) %>%
    column_spec(11, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
3-Points Attempts per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG x3Pm_36 x3Pm_G x3Pa_36 x3Pa_G x3P.
1 Steve Novak SF HOU 2007 - 2017 467 12.1 4.7 3.7 1.2 8.5 2.9 0.430
2 Stephen Curry PG GSW 2010 - 2018 574 34.7 22.8 3.5 3.3 7.9 7.6 0.438
3 Ryan Anderson PF ORL 2009 - 2018 556 26.5 13.2 2.9 2.1 7.6 5.6 0.381
4 Klay Thompson SG GSW 2012 - 2018 464 32.7 19.1 3.1 2.9 7.5 6.8 0.419
5 Patty Mills PG SAS 2010 - 2018 441 17.0 7.9 2.8 1.3 7.2 3.4 0.395
5 J.R. Smith SG DEN 2005 - 2018 880 27.0 12.9 2.7 2.0 7.2 5.4 0.374
7 C.J. Miles SG UTA 2006 - 2018 715 20.9 9.8 2.4 1.4 6.7 3.9 0.361
8 James Harden SG HOU 2010 - 2018 615 33.6 22.1 2.4 2.2 6.6 6.2 0.364
9 Danny Green SG SAS 2010 - 2018 470 25.0 8.8 2.6 1.8 6.5 4.5 0.400
9 Damon Jones PG CLE 1999 - 2009 657 20.5 6.6 2.5 1.4 6.5 3.7 0.390
AllTime3Pma %>%
    arrange(desc(x3Pm_36), desc(x3P.)) %>%
    mutate(Player = reorder(Player, desc(x3Pm_36), FUN=mean)) %>%
    head(n=10) %>%
    ggplot(aes(group = 1)) +
    geom_segment(aes(x=Player, xend=Player, y=x3Pm_36, yend=x3Pa_36), color="black") +
    geom_point(aes(Player, x3Pm_36, color="#FF5800"), size=5) +
    geom_point(aes(Player, x3Pa_36, color="#FF5800"), size=2.5) +
    geom_point(aes(Player, x3Pm_G, color="#009dff"), size=5, shape=18) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    ggtitle("3-Pts Made per 36 Minutes vs. 3-Pts Made per Game") +
    xlab("") +
    ylab("3-Pts Made") +
    scale_color_manual(name="", values=c("#009dff", "#FF5800"),
                       labels=c("3-Pts a/m per Game", "3-Pts a/m per 36 minutes"),
                       guide = guide_legend(reverse=TRUE)) +
    theme(legend.position='none')



BIG 3-Pointers Names


The Three-Pointers will be sorted according to the following arbritary criteria :

  • Annual league leader in 3-Points percentage category earn 1 point.
  • Top 10 all-time leaders in 3-Points percentage category earn 2 points.
  • Top 10 all-time leaders in 3-Points made category with 3P% more than 38% earn 1 point.
  • Top 10 all-time leaders in 3-Points made per 36 minutes category with 3P% more than 38% earn 1 point.
  • Annual Three-Points Contest winner earn 1 point.

Calculate the points and we have the rank!

Note that we have special category here that cannot be apply anywhere els, “Three-Points Contest winner”. It’s annual contest held during NBA All-Star Weekend. I had to add the winners data manually.


Let the calculation begin.

# Filter total 3-Points made with 3P% > .380
AllTime3Pm <- NBA %>%
    group_by(Player) %>%
    summarise(x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3P. = x3Pm / x3Pa) %>%
    filter(x3P. > 0.38) %>%
    arrange(desc(x3Pm)) %>% head(10)
# Filter 3-Points made per 36 minutes with 3P% > .380
AllTime3Pm36 <- AllTime3Pma %>%
    filter(x3P. >= 0.38) %>%
    arrange(desc(x3Pm_36)) %>%
    head(n=10)
# Create Three-Points Contest data frame
Year <- c(1986:1998, 2000:2018)
Player <- c(rep("Larry Bird", 3), "Dale Ellis", rep("Craig Hodges", 3), rep("Mark Price", 2), "Glen Rice", "Tim Legler", "Steve Kerr", rep("Jeff Hornacek", 2), "Ray Allen", rep("Peja Stojakovic", 2), "Voshon Lenard", "Quentin Richardson","Dirk Nowitzki", rep("Jason Kapono", 2), "Daequan Cook", "Paul Pierce", "James Jones", "Kevin Love", "Kyrie Irving", "Marco Belinelli", "Stephen Curry", "Klay Thompson", "Eric Gordon", "Devin Booker")
TPContest <- data.frame(Year, Player)
# Create all three-pointers leaders
Super3Pointers <- data.frame(Player = c(as.character(Top3Pts.$Player),
                               as.character(rep(head(AllTime3P.$Player, 10), 2)),
                               as.character(AllTime3Pm$Player),
                               as.character(AllTime3Pm36$Player),
                               as.character(TPContest$Player))) %>%
    count(Player) %>%
    arrange(desc(n))
# Display the final table:
    
Super3Pointers %>%
    group_by(Player) %>%
    mutate(Pos = getmode(NBA$Position[NBA$Player %in% Player]),
           x3Pm = sum(NBA$X3P[NBA$Player %in% Player], na.rm = T),
           x3Pa = sum(NBA$X3PA[NBA$Player %in% Player], na.rm = T),
           "3P%" = round(x3Pm/x3Pa, 3),
           Tokens = paste(strrep("|", n)),
           TotalTokens = n) %>%
    select(-"n") %>%
    kable(escape = FALSE, caption = "The Most Prominent Three-Pointers") %>%
    kable_styling(bootstrap_options = "striped", full_width = F, position = "center") %>%
    column_spec(1, bold = T) %>%
    column_spec(6, bold = T, color = "gold") %>%
    column_spec(7, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
The Most Prominent Three-Pointers
Player Pos x3Pm x3Pa 3P% Tokens TotalTokens
Kyle Korver SG 2049 4754 0.431 |||||||| 8
Jason Kapono SF 457 1054 0.434 |||||| 6
Craig Hodges SG 563 1408 0.400 ||||| 5
Stephen Curry PG 1917 4379 0.438 ||||| 5
Steve Kerr PG 726 1599 0.454 ||||| 5
Steve Novak SF 575 1337 0.430 |||| 4
Tim Legler SG 260 603 0.431 |||| 4
B.J. Armstrong PG 436 1026 0.425 ||| 3
Dale Ellis SF 1719 4266 0.403 ||| 3
Hubert Davis SG 728 1651 0.441 ||| 3
Larry Bird SF 649 1727 0.376 ||| 3
Peja Stojakovic SF 1760 4392 0.401 ||| 3
Dirk Nowitzki PF 1780 4668 0.381 || 2
Drazen Petrovic SG 255 583 0.437 || 2
Glen Rice SF 1565 3920 0.399 || 2
J.J. Redick SG 1271 3064 0.415 || 2
James Jones SF 776 1937 0.401 || 2
Jeff Hornacek SG 828 2055 0.403 || 2
Klay Thompson SG 1328 3171 0.419 || 2
Mark Price PG 976 2428 0.402 || 2
Ray Allen SG 2973 7429 0.400 || 2
Steve Nash PG 1685 3939 0.428 || 2
Anthony Morrow SG 807 1937 0.417 | 1
Anthony Peeler SG 940 2445 0.384 | 1
Brent Barry SG 1395 3442 0.405 | 1
Brian Taylor PG 157 417 0.376 | 1
Bruce Bowen SF 818 2082 0.393 | 1
Byron Scott SG 775 2093 0.370 | 1
Campy Russell SF 34 93 0.366 | 1
Chauncey Billups PG 1830 4725 0.387 | 1
Daequan Cook SG 442 1232 0.359 | 1
Damon Jones PG 941 2414 0.390 | 1
Dana Barros PG 1090 2652 0.411 | 1
Danny Green SG 849 2123 0.400 | 1
Darrell Griffith SG 530 1596 0.332 | 1
Dell Curry SG 1245 3098 0.402 | 1
Devin Booker SG 246 694 0.354 | 1
Eric Gordon SG 1023 2690 0.380 | 1
Fred Brown SG 110 295 0.373 | 1
Fred Hoiberg SG 373 941 0.396 | 1
Jason Terry SG 2242 5895 0.380 | 1
Jim Les PG 196 495 0.396 | 1
Jon Sundvold PG 226 576 0.392 | 1
Jose Calderon PG 861 2106 0.409 | 1
Kevin Love PF 887 2435 0.364 | 1
Kiki Vandeweghe SF 218 592 0.368 | 1
Kyrie Irving PG 723 1887 0.383 | 1
Marco Belinelli SG 853 2262 0.377 | 1
Matt Bonner PF 797 1923 0.414 | 1
Mike Dunleavy SF 1446 3879 0.373 | 1
Patty Mills PG 592 1499 0.395 | 1
Paul Pierce SF 2143 5816 0.368 | 1
Quentin Richardson SF 1167 3288 0.355 | 1
Rashard Lewis SF 1787 4625 0.386 | 1
Reggie Miller SG 2560 6486 0.395 | 1
Richard Hamilton SG 530 1530 0.346 | 1
Ryan Anderson PF 1184 3107 0.381 | 1
Steve Smith SG 1148 3208 0.358 | 1
Tracy Murray SF 753 1942 0.388 | 1
Voshon Lenard SG 936 2437 0.384 | 1

Now see their name cloud

Super3Pointers %>% wordcloud2(size=0.5, color='random-light', backgroundColor="black")





All 3-Point Shooters Career Performance


I can’t resist to see how all the three-pointers compares year-by-year, so let’s see their career performance.

NBA %>%
    filter(Player %in% Super3Pointers$Player) %>%
    group_by(Age, Player) %>%
    summarise(x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3P. = x3Pm/x3Pa) %>%
    filter(x3P. < 0.7) %>%
    ggplot(aes(Age, Player, fill=x3P.)) +
    geom_tile(color = "grey50") +
    scale_x_continuous(expand = c(0, 0)) +
    scale_fill_gradientn(colors = brewer.pal(9, "Reds")) +
    theme(panel.grid = element_blank()) +
    scale_y_discrete(limits = rev(levels(Player))) +
    ggtitle("All 3-Point Shooters Career Performance") +
    theme(legend.position="bottom")



3-Point Ratio

Don’t you dare to think that I’m done with the three-points, it’s the most interesting subject of all. So like it or not, I’m gonna see the highest 3-point ratio. It measure how many percent of points come from the behind the 3-points line.

NBA %>%
    filter(Year >= 1980 & X3P > 1) %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Position),
              Team = getmode(Tm),
              Games = sum(G),
              Pts2 = sum(X2P) * 2,
              Pts3 = sum(X3P) * 3,
              PtsFT = sum(FT),
              PTS = sum(PTS),
              PPG = round(PTS / Games, 1),
              Pts2r = round(Pts2 / PTS, 3),
              Pts3r = round(Pts3 / PTS, 3),
              PtsFTr = round(PtsFT / PTS, 3),
              x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3P. = round(x3Pm / x3Pa, 3)) %>%
    filter(x3Pm > 250) %>%
    select(-c(x3Pm, x3Pa, Pts2:PtsFT)) %>%
    arrange(desc(Pts3r)) %>%
    mutate(Player = reorder(Player, Pts3r, FUN=mean)) %>%
    head(10) %>%
    gather(Parameter, Value, Pts2r:PtsFTr) %>%
    ggplot(aes(Player, Value, fill=forcats::fct_rev(Parameter))) +
    geom_bar( stat="identity", position="fill") +
    geom_label(aes(label = paste(Value * 100, "%")),
               stat = 'identity',
               position = position_stack(vjust = 0.5),
               fill = "#FF4000",
               color = "#00FFFF",
               size = 2.5) +
    ggtitle("Highest 3-Points Ratio") +
    coord_flip() +
    xlab("") +
    ylab("Shooting") +
    guides(fill = guide_legend(reverse = TRUE)) +
    theme(legend.position="bottom") +
    theme(legend.title=element_blank())


Still not satisfied enough, I’m going to see same plot but filled with the top dozen best three-pointers in the league, minus the name that already appear in the plot above.

NBA %>%
    filter(Year >= 1980 & Player %in% head(Super3Pointers$Player, 12)) %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              Games = sum(G),
              Pts2 = sum(X2P) * 2,
              Pts3 = sum(X3P) * 3,
              PtsFT = sum(FT),
              PTS = sum(PTS),
              PPG = round(PTS / Games, 1),
              Pts2r = round(Pts2 / PTS, 3),
              Pts3r = round(Pts3 / PTS, 3),
              PtsFTr = round(PtsFT / PTS, 3),
              x3Pm = sum(X3P),
              x3Pa = sum(X3PA),
              x3P. = round(x3Pm / x3Pa, 3)) %>%
    filter(x3Pm > 250, Player != c("Steve Novak", "Kyle Korver")) %>%
    select(-c(x3Pm, x3Pa, Pts2:PtsFT)) %>%
    arrange(desc(Pts3r)) %>%
    mutate(Player = factor(Player, levels = c("Larry Bird", "B.J. Armstrong", "Dale Ellis", "Craig Hodges", "Tim Legler", "Peja Stojakovic", "Hubert Davis", "Steve Kerr", "Jason Kapono", "Stephen Curry"))) %>%
    gather(Parameter, Value, Pts2r:PtsFTr) %>%
    ggplot(aes(Player, Value, fill=forcats::fct_rev(Parameter))) +
    geom_bar( stat="identity", position="fill") +
    geom_label(aes(label = paste(Value * 100, "%")),
               stat = 'identity',
               position = position_stack(vjust = 0.5),
               fill = "#FF4000",
               color = "#00FFFF",
               size = 2.5) +
    ggtitle("Shooting Ratio of the Super3Pointers") +
    coord_flip() +
    xlab("") +
    ylab("Shooting") +
    guides(fill = guide_legend(reverse = TRUE)) +
    theme(legend.position="bottom") +
    theme(legend.title=element_blank())

It’s interesting to see that, despite being a three consecutive three-point contest winner, and those amazing threes we saw in the NBA highlight, Larry Bird actually acquired less than one-tenth of his points from behind the three-point line.




3-Point Rate


While in the 3-point ratio we can see how many percent of points earned from 3-point, in 3-point rate we can see players who loves to take long-range shooting attempts more than shorter distance.

x3PrateAVG <- NBA %>%
    summarise(Player = "Average",
              Games = sum(G, na.rm = T),
              FGa = sum(FGA, na.rm = T),
              x3Pa = sum(X3PA, na.rm = T),
              FGapG = round(FGa / Games, 2),
              x3PapG = round(x3Pa / Games, 2),
              x3Pr = round(x3Pa / FGa, 3)) %>%
    select(-c(Games, x3Pa, FGa))
x3Prate <- NBA %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              Games = sum(G),
              PTS = sum(PTS),
              PPG = round(PTS / Games, 1),
              FGa = sum(FGA),
              x3Pa = sum(X3PA),
              x3Pr = round(x3Pa / FGa, 3),
              x3PapG = round(x3Pa / Games, 2),
              FGapG = round(FGa / Games, 2)) %>%
    filter(x3Pa > 250) %>%
    mutate(Rank = min_rank(desc(x3Pr))) %>%
    select(Rank, everything()) %>%
    arrange(Rank) %>%
    head(10) %>%
    bind_rows(x3PrateAVG)
LabelFix <- x3Prate %>%
    mutate(x3PapG = x3PapG,
           x3Pr = paste(x3Pr * 100, "%")) %>%
    gather(Parameter, Count, x3PapG, -c(Player, x3Pr)) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(x3Pr))])))
x3Prate %>%
    gather(Parameter, Count, FGapG:x3PapG, -c(Player, x3Pr)) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(x3Pr))]))) %>%
    ggplot(aes(Player, Count, fill = Parameter, label = x3Pr)) +
    geom_bar(position="dodge", stat="identity") +
    geom_label(aes(label = x3Pr, y = 0.7),
              data = LabelFix,
              stat = 'identity',
              position = "stack",
              fill = "#FFFFFF",
              color = "#000000",
              size = 4) +
    ggtitle("3-Point Rate") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    ylab("3P/FG per game") +
    scale_fill_manual(name="", values=c("#0000FF", "#008000"),
                       labels=c("FG per Game", "3P per Game")) +
    theme(legend.position="bottom")

NA

Now I’m finally satisfied having seen everything I wanted to see (so far), now we marching forward to see the next category.






Mixed


Effective Field Goal Percentage


eFG. (Effective Field Goal Percentage): This statistic adjusts for the fact that a 3-point field goal is worth one more point than a 2-point field goal. For example, suppose Player A goes 4 for 10 with 2 threes, while Player B goes 5 for 10 with 0 threes. Each player would have 10 points from field goals, and thus would have the same effective field goal percentage (50%).

The formula is \(\frac{FG + 0.5 * 3P}{FGA}\)


Creating table:

NBA %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              ActiveYears = paste(getmode(YearStart), "-", getmode(YearEnd)),
              Games = sum(G),
              PTS = sum(PTS),
              x2Pm = sum(X2P),
              x2Pa = sum(X2PA),
              FGm = sum(FG),
              FGa = sum(FGA),
              x3Pm = sum(X3P, na.rm = T),
              x3Pa = sum(X3PA, na.rm = T),
              PPG = round(PTS/Games, 1),
              x2P. = round(x2Pm/x2Pa, 3),
              x3P. = round(x3Pm/x3Pa, 3),
              eFG. = round((FGm + 0.5 * x3Pm) / FGa, 3)) %>%
    filter(Games > 400) %>%
    select(-c(PTS:x3Pa)) %>%
    arrange(desc(eFG.)) %>%
    head(20) %>%
    mutate(Rank = min_rank(desc(eFG.))) %>%
    select(Rank, everything()) %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "Effective Field Goal Percentage Leaders") %>%
    kable_styling("striped", full_width = T) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(2, bold = T) %>%
    column_spec(10, bold = T, color = "white", background = "#777777") %>%
    scroll_box(width = "100%", height = "300px")
Effective Field Goal Percentage Leaders
Rank Player Pos Team ActiveYears Games PPG x2P. x3P. eFG.
1 DeAndre Jordan C LAC 2009 - 2018 673 9.1 0.679 0.091 0.677
2 Steve Novak SF HOU 2007 - 2017 467 4.7 0.463 0.430 0.605
3 Artis Gilmore C CHI 1972 - 1988 909 17.1 0.600 0.077 0.599
4 Andris Biedrins C GSW 2005 - 2014 516 6.3 0.595 0.000 0.594
4 Tyson Chandler C CHI 2002 - 2018 1033 8.7 0.595 0.000 0.594
6 Amir Johnson PF TOR 2006 - 2018 745 7.5 0.590 0.340 0.585
6 Dwight Howard C ORL 2005 - 2018 954 17.5 0.588 0.089 0.585
8 Ryan Hollins C CHA 2007 - 2016 518 3.7 0.584 0.000 0.584
9 Shaquille O'Neal C LAL 1993 - 2011 1207 23.7 0.583 0.045 0.582
10 Mark West C PHO 1984 - 2000 1090 5.7 0.581 0.000 0.580
11 Stephen Curry PG GSW 2010 - 2018 574 22.8 0.507 0.438 0.575
12 Darryl Dawkins C PHI 1976 - 1989 726 12.0 0.573 0.133 0.572
12 Steve Johnson C KCK 1982 - 1991 626 11.7 0.573 0.000 0.572
14 James Donaldson C DAL 1981 - 1995 957 8.6 0.571 NaN 0.571
14 Kyle Korver SG PHI 2004 - 2018 1031 10.0 0.461 0.431 0.571
16 Brent Barry SG SEA 1996 - 2009 912 9.3 0.525 0.405 0.570
16 Matt Bonner PF SAS 2005 - 2016 792 5.8 0.516 0.414 0.570
18 Bo Outlaw PF ORL 1994 - 2008 914 5.4 0.573 0.079 0.568
19 Steve Kerr PG CHI 1989 - 2003 910 6.0 0.494 0.454 0.564
20 Ed Davis PF TOR 2011 - 2018 481 6.8 0.562 0.000 0.562


eFGLeaders <- NBA %>%
    group_by(Player) %>%
    summarise(Games = sum(G),
              x2Px = round(sum(X2P) / sum(FGA), 3),
              x3Px = round((sum(X3P, na.rm = T) * 1.5) / sum(FGA), 3),
              FGax = round((sum(FGA) - (sum(X2P) + (sum(X3P, na.rm = T) * 1.5))) / sum(FGA), 3),
              eFG. = round((sum(X2P) + sum(X3P, na.rm = T) * 1.5) / sum(FGA), 3)) %>%
    filter(Games > 400) %>%
    arrange(desc(eFG.)) %>%
    head(20) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(eFG.))]))) %>%
    gather(Parameter, Value, x2Px:FGax, -c(Games))
LabelFix1 <- eFGLeaders %>%
    spread(Parameter, Value) %>%
    mutate(eFG. = paste(eFG. * 100, "%"))
eFGLeaders %>%
    ggplot(aes(Player, Value, fill = Parameter)) + 
    geom_bar(stat = 'identity') +
    geom_text(aes(label = eFG., y = 0.95), size = 3) +
    ggtitle("Effective Field Goal Percentage") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    ylab("Percentage") +
    scale_fill_manual(labels=c("Missed  ", "2P  ", "3P"), values=c("#0080FF", "#0000FF", "#008000")) +
    theme(legend.title=element_blank()) +
    theme(legend.position="bottom")




Sharpest Shooters


Still obsessing with shooters, I’d like to find the sharpest shooter(s) in the league. To achieve this, I need to negate dunks, lay-ups, even hooks. I only interested in medium to long range shooters for this purpose. Therefore, I simply dismiss FG and take average of both 3-point % and Free Throw % as a measurement variable.

SharpestShooters <- NBA %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Pos),
              x3Pm = sum(X3P, na.rm = T),
              x3Pa = sum(X3PA, na.rm = T),
              FTm = sum(FT, na.rm = T),
              FTa = sum(FTA, na.rm = T),
              x3P. = round(x3Pm/x3Pa, 3),
              FT. = round(FTm/FTa, 3),
              Shoot. = round((x3P. + FT.)/2, 3)) %>%
    filter(x3Pa > 250 & FTa > 1000 & FT. > 0.85 & x3P. > 0.38) %>%
    arrange(desc(Shoot.)) %>%
    mutate(Rank = min_rank(desc(Shoot.))) %>%
    select(Rank, everything(), -c(x3Pm:FTa)) %>%
    head(20) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(Shoot.))])))
SharpestShooters %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "Sharpest Shooters") %>%
    kable_styling("striped", full_width = F, position = "float_left") %>%
    column_spec(2, bold = T) %>%
    column_spec(1, bold = T, color = "yellow", background = "#FF0000") %>%
    column_spec(6, bold = T, color = "white", background = "#777777")
Sharpest Shooters
Rank Player Pos x3P. FT. Shoot.
1 Stephen Curry PG 0.438 0.901 0.670
2 Steve Nash PG 0.428 0.904 0.666
3 Kyle Korver SG 0.431 0.880 0.656
4 Mark Price PG 0.402 0.904 0.653
5 J.J. Redick SG 0.415 0.888 0.652
6 Peja Stojakovic SF 0.401 0.895 0.648
7 Ray Allen SG 0.400 0.894 0.647
8 Jose Calderon PG 0.409 0.875 0.642
8 Reggie Miller SG 0.395 0.888 0.642
10 Chauncey Billups PG 0.387 0.894 0.641
11 B.J. Armstrong PG 0.425 0.856 0.640
11 Jeff Hornacek SG 0.403 0.877 0.640
13 Klay Thompson SG 0.419 0.853 0.636
14 Dana Barros PG 0.411 0.858 0.634
15 Allan Houston SG 0.402 0.863 0.633
15 Wally Szczerbiak SF 0.406 0.860 0.633
17 Hersey Hawkins SG 0.394 0.870 0.632
18 Dirk Nowitzki PF 0.381 0.879 0.630
19 Ben Gordon SG 0.401 0.857 0.629
20 Kyrie Irving PG 0.383 0.873 0.628

SharpestShooters %>%
    ggplot(aes(x3P., FT., size = Shoot., color=Pos, label=Player)) +
    geom_point(shape=18) +
    geom_text(size=3, color="#000000", position = position_nudge(y = -0.002)) +
    scale_color_manual(values = c("#FFA500", "#DDDD00" ,"#0000FF", "#32CD32")) +
    ggtitle("Sharpest Shooters") +
    xlab("3P%") +
    ylab("FT%") +
    theme(legend.position='none')






End of Session


LS0tDQp0aXRsZTogJ1Zpc3VhbGl6aW5nIE5CQSBTZWFzb25zIFBhcnQgNDogU2hvb3RpbmdzJw0KYXV0aG9yOiAiTnVubm8gTnVncm9obyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjc3M6IHN0eWxlLmNzcw0KICAgIHRoZW1lOiBwYXBlcg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICBodG1sX2RvY3VtZW50Og0KICAgIGNzczogc3R5bGUuY3NzDQogICAgdGhlbWU6IHBhcGVyDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KPGJyLz4NCjxici8+DQoNCjxkaXYgY2xhc3M9ImhlYWRlciI+DQoNCjxjZW50ZXI+PGltZyBzcmM9ImltYWdlcy9OQkFMb2dvVHJhbnNwLnBuZyIgYWx0PSJkcmF3aW5nIiB3aWR0aD0iMjAwcHgiIGhlaWd0aD0iMTAwcHgiLz4NCg0KKipTZWFzb25zIDE5NTAtMjAxNyoqDQoNCjxkaXYgY2xhc3M9ImRyb3Bkb3duIj4NCg0KPGJ1dHRvbiBjbGFzcz0iZHJvcGJ0biI+UGFydCA0OiBTaG9vdGluZ3MgKDMtUG9pbnRzKTwvYnV0dG9uPg0KDQo8ZGl2IGNsYXNzPSJkcm9wZG93bi1jb250ZW50Ij4NCg0KPGEgaHJlZj0iaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnMwIj5JbnRyb2R1Y3Rpb24gJiBQcmVwYXJhdGlvbjwvYT4NCg0KPGEgaHJlZj0iaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnMxIj5QYXJ0IDE6IFBsYXllcnM8L2E+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zMiI+UGFydCAyOiBQb2ludHM8L2E+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zMyI+UGFydCAzOiBTaG9vdGluZ3MgKEZHIGFuZCBGVCk8L2E+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zNSI+UGFydCA1OiBTa2lsbHM8L2E+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zNiI+UGFydCA2OiBSb2xlczwvYT4NCg0KPC9kaXY+DQoNCjwvZGl2Pg0KDQo8L2NlbnRlcj4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQotLS0NCg0KPGJyLz4NCg0KIyBTZXR1cCBTdGFnZQ0KDQo8YnIvPg0KDQpMb2FkaW5nIG5lY2Vzc2FyeSBwYWNrYWdlcy4NCg0KYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdncmlkZ2VzKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0KbGlicmFyeShmb3JjYXRzKQ0KYGBgDQoNCjxici8+DQoNCkNyZWF0ZSBhZGRpdGlvbmFsIGZ1bmN0aW9ucw0KDQpgYGB7cn0NCiMgTW9kZSBhdmVyYWdlDQpnZXRtb2RlIDwtIGZ1bmN0aW9uKHYpIHsNCiAgIHVuaXF2IDwtIHVuaXF1ZSh2KQ0KICAgdW5pcXZbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHYsIHVuaXF2KSkpXQ0KfQ0KYGBgDQoNCjxici8+DQoNCiMjIyMgTG9hZCB0aGUgZGF0YXNldA0KDQo8YnIvPg0KDQpEYXRhc2V0IHRha2VuIGZyb206IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZHJnaWxlcm1vL25iYS1wbGF5ZXJzLXN0YXRzL3ZlcnNpb24vMg0KDQpJZiB5b3UgbmVlZCB0byBzZWUgdGhlIGdsb3NzYXJ5IGFuZCB0aGUgZGF0YSB0aWR5aW5nIHByb2Nlc3MsIHBsZWFzZSB2aXNpdCBmaXJzdCBwYXJ0IFtoZXJlXShodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczApLg0KDQpMb2FkaW5nIGRhdGFzZXRzLi4uDQoNCmBgYHtyfQ0KTkJBIDwtIHJlYWQuY3N2KCJOQkFfVGlkeVNldC5jc3YiKQ0KTkJBX1NjYWxlZCA8LSByZWFkLmNzdigiTkJBX1NjYWxlZF9UaWR5U2V0LmNzdiIpDQpOQkEkUG9zIDwtIGZhY3RvcihOQkEkUG9zLCBsZXZlbHMgPSBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIikpDQpQb3NDb2xvckNvZGUgPC0gYygiQyI9IiNGRjAwMDAiLCAiUEYiPSIjRkZBNTAwIiwgIlNGIj0iI0REREQwMCIgLCJTRyI9IiMwMDAwRkYiLCAiUEciPSIjMzJDRDMyIikNCmBgYA0KDQo8YnIvPg0KDQoqKkRpc3BsYXlpbmcgbWFpbiB0YWJsZSoqDQoNCjxkaXYgY2xhc3M9J21haW50YWJsZSc+DQoNCmBgYHtyfQ0KTkJBDQpgYGANCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KLS0tDQoNCjxici8+DQoNCiMjIFNIT09USU5HDQoNCjxici8+DQoNCkluIHRoaXMgc2Vzc2lvbiB3ZSBleHBsb3JlIHNob290aW5nIHN0YXRpc3RpY3Mgb2YgdGhlIE5CQSBwbGF5ZXJzLiBBcyBkaXNjdXNzZWQgaW4gdGhlIFtwcmV2aW91cyBzZXNzaW9uXShodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczMpLCB0aGVyZSBhcmUgMyBiYXNpYyBzaG9vdGluZyBjYXRlZ29yeSBpbiBiYXNrZXRiYWxsOiBbRmllbGQgR29hbHMgKEZHKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRmllbGRfZ29hbF8oYmFza2V0YmFsbCkpLCBbVGhyZWUtUG9pbnRzIEZpZWxkIEdvYWwgKDNQKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVGhyZWUtcG9pbnRfZmllbGRfZ29hbCksIGFuZCBbRnJlZS1UaHJvdyAoRlQpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GcmVlX3Rocm93KS4gTGFzdCBzZXNzaW9uIHdlIGFscmVhZHkgc2VlIHRoZSBleHBsb3JhdG9yeSBhbmFseXNpcyBvZiBGRyBhbmQgRlQsIG5vdyBpdCdzIHRpbWUgdG9vIHNlZSAzLVBvaW50cyBzdGF0cyB3aXRoIGFkZGl0aW9uYWwgbWl4ZWQgc2hvb3Rpbmcgc3RhdHMuDQoNCkFnYWluLCBJIHVzZSBiYXNpYyBSR0IgYXMgY29sb3ItY29kZSBmb3Igc2hvb3RpbmcgdHlwZTogcmVkIGZvciBGVCwgZ3JlZW4gZm9yIDNQIGFuZCBibHVlIGZvciBGRy4gDQoNCioqTk9URTogV2hpbGUgSSBkb24ndCBoYXZlIHRpbWUgb3IgYW4gaW5zcGlyYXRpb24gYWJvdXQgdGhlIG5hcnJhdGl2ZSwgW0xvcmVtIGlwc3VtIHRleHRzXShodHRwczovL3d3dy5saXBzdW0uY29tLykgd2lsbCBiZSBpbiBkaXNwbGF5LioqDQoNCjxici8+DQo8YnIvPg0KDQojIyBUaHJlZS1Qb2ludHMNCg0KPGJyLz4NCg0KU2luY2UgdGhyZWUtcG9pbnRzIGlzIG15IGZhdm9yaXRlIGNhdGVnb3J5IGluIGEgYmFza2V0YmFsbCBzdGF0aXN0aWNzLCBJIHdpbCBzZWxmaXNobHkgZGlnIGRlZXBlciB0aGFuIGFueSBvdGhlciBzaG9vdGluZyBjYXRlZ29yeS4gU28gaGVyZSB3ZSBnby4NCg0KPGJyLz4NCg0KIyMjIEFubnVhbCAzLVBvaW50cyAlIExlYWd1ZSBMZWFkZXJzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCk5vIHF1aSBhdXRlbSByZXF1ZS4gRWEgdGFudGFzIHZvY2VudCBkb2xvcmVzIHZpbSwgYW4gdml2ZW5kdW0gZGVzZXJ1bnQgc2Vuc2lidXMgdml4LiBFYSBtZWwgenJpbCBhbGlxdWFtIGFwcGVsbGFudHVyLCB1dGluYW0gb21uZXNxdWUgbGliZXJhdmlzc2UgcGVyIGVhLiBDb25zdWwgcmlkZW5zIHZvbHVwdGF0dW0gZWEgcGVyLCBsYW9yZWV0IG5vbWluYXRpIHNhbHV0YW5kaSBuZSBoaXMuIEFzc3VtIGdyYWVjaXMgdm9sdXB0YXRpYnVzIGlkIHNpdCwgY3VtIGlkIGNvbnN0aXR1dG8gYWRpcGlzY2luZy4NCg0KPGRpdiBjbGFzcyJ0YWJsZSI+DQoNCmBgYHtyfQ0KVG9wM1B0cy4gPC0gTkJBICU+JQ0KICAgIGZpbHRlcihZZWFyID49IDE5ODAgJiBYM1AuID4gMCkgJT4lDQogICAgZ3JvdXBfYnkoWWVhciwgUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoUG9zID0gZ2V0bW9kZShQb3MpLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBNUEcgPSByb3VuZChzdW0oTVApL0dhbWVzLCAxKSwNCiAgICAgICAgICAgICAgUFBHID0gcm91bmQoc3VtKFBUUykvR2FtZXMsIDEpLA0KICAgICAgICAgICAgICB4M1BtID0gc3VtKFgzUCksDQogICAgICAgICAgICAgIHgzUGEgPSBzdW0oWDNQQSksDQogICAgICAgICAgICAgIHgzUHQuID0gcm91bmQoeDNQbS94M1BhLCAzKSkgJT4lDQogICAgZmlsdGVyKGlmZWxzZShZZWFyICVpbiUgMTk4MDoxOTkwLCB4M1BtID49IDI1LA0KICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTkxOjE5OTQsIHgzUG0gPj0gNTAsDQogICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgYygxOTk4LCAyMDAwLCAyMDA0OjIwMDgsIDIwMTApLCB4M1BtID49IDU1LA0KICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk5OSwgeDNQbSA+PSAzNCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMjAxMiwgeDNQbSA+PSA0NCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeDNQbSA+PSA4MikpKSkpKSAlPiUNCiAgICBncm91cF9ieShZZWFyKSAlPiUNCiAgICBzdW1tYXJpc2UoUGxheWVyID0gUGxheWVyW3doaWNoLm1heCh4M1B0LildLA0KICAgICAgICAgICAgICBUZWFtID0gVGVhbVt3aGljaC5tYXgoeDNQdC4pXSwNCiAgICAgICAgICAgICAgUG9zID0gUG9zW3doaWNoLm1heCh4M1B0LildLA0KICAgICAgICAgICAgICBHYW1lcyA9IEdhbWVzW3doaWNoLm1heCh4M1B0LildLA0KICAgICAgICAgICAgICBNUEcgPSBNUEdbd2hpY2gubWF4KHgzUHQuKV0sDQogICAgICAgICAgICAgIFBQRyA9IFBQR1t3aGljaC5tYXgoeDNQdC4pXSwNCiAgICAgICAgICAgICAgeDNQbSA9IHgzUG1bd2hpY2gubWF4KHgzUHQuKV0sDQogICAgICAgICAgICAgIHgzUGEgPSB4M1BhW3doaWNoLm1heCh4M1B0LildLA0KICAgICAgICAgICAgICB4M1B0LiA9IG1heCh4M1B0LiwgbmEucm0gPSBUKSkgJT4lDQogICAgYXJyYW5nZShkZXNjKFllYXIpKQ0KDQpUb3AzUHRzLiAlPiUNCiAgICBtdXRhdGUoUG9zID0gY2VsbF9zcGVjKFBvcywgY29sb3IgPSAid2hpdGUiLCBhbGlnbiA9ICJjIiwgDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSBmYWN0b3IoUG9zLCBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc0NvbG9yQ29kZSkpKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJBbm51YWwgMy1Qb2ludHMgJSBMZWFndWUgTGVhZGVycyIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFQsIGNvbG9yID0gInllbGxvdyIsIGJhY2tncm91bmQgPSAiI0ZGMDAwMCIpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDIsIGJvbGQgPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxMCwgYm9sZCA9IFQsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9ICIjNzc3Nzc3IikgJT4lDQogICAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikNCmBgYA0KDQoqVG8gcXVhbGlmeSBmb3IgbGVhZ3VlIGxlYWRlcnMsIGEgcGxheWVyIG11c3QgbWVldCB0aGUgW1JhdGUgU3RhdGlzdGljIFJlcXVpcmVtZW50c10oaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2Fib3V0L3JhdGVfc3RhdF9yZXEuaHRtbCkqDQo8L2Rpdj4NCg0KPGJyLz4NCg0KVml4IGRvbWluZyBjb25jZXB0YW0gcGVyY2lwaXR1ciBjdSwgcXVpIGVzc2VudCB2aWRpc3NlIG51c3F1YW0gZXQhIFZpdmVuZG8gdm9sdXB0YXJpYSBzY3JpYmVudHVyIHNpdCB1dC4gVmlzIG1vZHVzIGNldGVyb3MgY29udGVudGlvbmVzIGF0LCBpZCByZWdpb25lIG1vZGVyYXRpdXMgc2VhPyBUZSB2aW0gbmF0dW0gaGVuZHJlcml0Lg0KDQpgYGB7ciB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA0LjV9DQptZWFuM1BULlllYXIgPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFllYXIpICU+JQ0KICAgIGZpbHRlcihZZWFyID49IDE5ODAgJiBYM1AuID4gMCkgJT4lDQogICAgc3VtbWFyaXNlKHgzUFQuID0gc3VtKFgzUCwgbmEucm0gPSBUKS9zdW0oWDNQQSwgbmEucm0gPSBUKSkNCg0KVDNwLiA8LSBUb3AzUHRzLiAlPiUNCiAgICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9iYXIoYWVzKFllYXIsIHgzUHQuLCBmaWxsID0gUG9zLCB0ZXh0ID0gcGFzdGUoIlBsYXllcjoiLCBQbGF5ZXIpKSwgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgICBnZW9tX2xpbmUoZGF0YT1tZWFuM1BULlllYXIsIGFlcyhZZWFyLCB4M1BULiwgbGluZXR5cGUgPSAiQXZlcmFnZSBsaW5lIiksIGNvbG9yID0gImJsYWNrIikgKw0KICAgIGdndGl0bGUoIkFubnVhbCAzLXBvaW50IFNob290aW5nIExlYWRlcnMiKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihUb3AzUHRzLiR4M1B0LiksIGNvbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5NTAsIDIwMTcsIDEwKSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKCJQb3MiLCB2YWx1ZXMgPSBQb3NDb2xvckNvZGUpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQ0KDQpwVDNwLiA8LSBnZ3Bsb3RseShUM3AuKQ0KI2FwaV9jcmVhdGUocFQzcC4sIGZpbGVuYW1lID0gIkFubnVhbDNQJSBMZWFkZXJzIikNCmBgYA0KDQo8ZGl2PjxhIGhyZWY9Imh0dHBzOi8vcGxvdC5seS9+TnVuTm9EZUNhYnVuaWMvMjIvP3NoYXJlX2tleT1kQ1hNOWFFN3VyNWJYNnAzOWZuODhKIiB0YXJnZXQ9Il9ibGFuayIgdGl0bGU9IkFubnVhbDNQJSBMZWFkZXJzIiBzdHlsZT0iZGlzcGxheTogYmxvY2s7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxpbWcgc3JjPSJodHRwczovL3Bsb3QubHkvfk51bk5vRGVDYWJ1bmljLzIyLnBuZz9zaGFyZV9rZXk9ZENYTTlhRTd1cjViWDZwMzlmbjg4SiIgYWx0PSJBbm51YWwzUCUgTGVhZGVycyIgc3R5bGU9Im1heC13aWR0aDogMTAwJTt3aWR0aDogODgwcHg7IiAgd2lkdGg9Ijg4MCIgb25lcnJvcj0idGhpcy5vbmVycm9yPW51bGw7dGhpcy5zcmM9J2h0dHBzOi8vcGxvdC5seS80MDQucG5nJzsiIC8+PC9hPjxzY3JpcHQgZGF0YS1wbG90bHk9Ik51bk5vRGVDYWJ1bmljOjIyIiBzaGFyZWtleS1wbG90bHk9ImRDWE05YUU3dXI1Ylg2cDM5Zm44OEoiIHNyYz0iaHR0cHM6Ly9wbG90Lmx5L2VtYmVkLmpzIiBhc3luYz48L3NjcmlwdD48L2Rpdj4NCg0KPGJyLz4NCg0KPGRpdiBjbGFzcz0iRmFjdCI+DQoNCjx1bCBjbGFzcz0iQ3VzdG9tTGlzdCI+DQoNCjxsaT5BdmVyYWdlIHRvcCAzUCUgaXMgYHIgcm91bmQoc3VtKFRvcDNQdHMuJHgzUG0pL3N1bShUb3AzUHRzLiR4M1BhKSwgMylgLjwvbGk+DQo8bGk+SGlnaGVzdCB0b3AgM1AlIGlzIGByIG1heChUb3AzUHRzLiR4M1B0LilgLCBpbiBzZWFzb24gYHIgVG9wM1B0cy4kWWVhclt3aGljaC5tYXgoVG9wM1B0cy4keDNQdC4pXWAuPC9saT4NCjxsaT5Mb3dlc3QgdG9wIDNQJSBpcyBgciBtaW4oVG9wM1B0cy4keDNQdC4pYCwgaW4gc2Vhc29uIGByIFRvcDNQdHMuJFllYXJbd2hpY2gubWluKFRvcDNQdHMuJHgzUHQuKV1gLjwvbGk+DQo8bGk+VGhlIDxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogIzAwMDBGRjt0ZXh0LWFsaWduOiBjOyIgPlNHPC9zcGFuPiBkb21pbmF0ZWQgdGhlIGxlYWd1ZSBieSBgciByb3VuZChucm93KFRvcDNQdHMuW1RvcDNQdHMuJFBvcyA9PSAiU0ciLF0pL25yb3coVG9wM1B0cy4pKjEwMCwgMSlgJSwgZm9sbG93ZWQgYnkgPHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjMzJDRDMyO3RleHQtYWxpZ246IGM7IiA+UEc8L3NwYW4+IHdpdGggYHIgcm91bmQobnJvdyhUb3AzUHRzLltUb3AzUHRzLiRQb3MgPT0gIlBHIixdKS9ucm93KFRvcDNQdHMuKSoxMDAsIDEpYCUsIHRoZW4gPHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjRERERDAwO3RleHQtYWxpZ246IGM7IiA+U0Y8L3NwYW4+PGJyLz48c3Bhbj4gYWRkZWQgd2l0aCBgciByb3VuZChucm93KFRvcDNQdHMuW1RvcDNQdHMuJFBvcyA9PSAiU0YiLF0pL25yb3coVG9wM1B0cy4pKjEwMCwgMSlgJSwgYW5kIGZpbmFsbHkgPHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjRkZBNTAwO3RleHQtYWxpZ246IGM7IiA+UEY8L3NwYW4+IGNvbXBsZXRlZCB0aGUgbGVhZGVyYm9hcmQgd2l0aCBvbmx5IGByIHJvdW5kKG5yb3coVG9wM1B0cy5bVG9wM1B0cy4kUG9zID09ICJQRiIsXSkvbnJvdyhUb3AzUHRzLikqMTAwLCAxKWAlLiBObyBjZW50ZXIgZXZlciBiZSBpbiAzUCUgbGVhZGVyYm9hcmQsIHRoaXMgYmVjYXVzZSAzLVBvaW50cyBpcyByZXF1aXJlcyBtb3JlIGxvbmctcmFuZ2Ugc2hvb3Rpbmcgc2tpbGxzIHdoaWNoIG5vdCB0aGUgcm9sZSBmb3IgY2VudGVycy48L2xpPg0KICAgIA0KPC91bD4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyBBbGwtVGltZSAzIFBvaW50cyBGaWVsZCBHb2FscyAlIExlYWRlcnMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KU2VkIHR1cnBpcyBlc3QsIHByZXRpdW0gbmVjIGVsaXQgbGFjaW5pYSwgYWNjdW1zYW4gdHJpc3RpcXVlIGVuaW0uIEN1cmFiaXR1ciBjb25zZWN0ZXR1ciByaXN1cyBzaXQgYW1ldCBtYXVyaXMgdmVzdGlidWx1bSByaG9uY3VzLiBOYW0gbm9uIGZpbmlidXMgYXVndWUsIG5lYyByaG9uY3VzIG51bGxhLiBOYW0gaW4gbW9sZXN0aWUgaXBzdW0uIFByYWVzZW50IHBoYXJldHJhIGZlcm1lbnR1bSBvcmNpLCBldCBmYXVjaWJ1cyBvZGlvIHBvcnRhIG5vbi4gRHVpcyBmaW5pYnVzIG1hdHRpcyBlbGVtZW50dW0uIE1hdXJpcyBwb3N1ZXJlIGRvbG9yIG5lYyBhbGlxdWFtIG1hdHRpcy4gRXRpYW0gZWxlbWVudHVtIGVyYXQgZXgsIGF0IGRhcGlidXMgbGFjdXMgY29udmFsbGlzIGFsaXF1YW0uIE5hbSBlZ2V0IG1hZ25hIGxhY3VzLg0KDQo8ZGl2IGNsYXNzPSJ0YWJsZSI+DQoNCmBgYHtyfQ0KQWxsVGltZTNQLiA8LSBOQkEgJT4lDQogICAgZmlsdGVyKFgzUC4gPiAwKSAlPiUNCiAgICBncm91cF9ieShQbGF5ZXIpICU+JQ0KICAgIHN1bW1hcmlzZShQb3MgPSBnZXRtb2RlKFBvcyksDQogICAgICAgICAgICAgIFRlYW0gPSBnZXRtb2RlKFRtKSwNCiAgICAgICAgICAgICAgQWN0aXZlWWVhcnMgPSBwYXN0ZShnZXRtb2RlKFllYXJTdGFydCksICItIiwgZ2V0bW9kZShZZWFyRW5kKSksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBNUEcgPSByb3VuZChzdW0oTVApIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBQUEcgPSByb3VuZChzdW0oUFRTKSAvIEdhbWVzLCAxKSwNCiAgICAgICAgICAgICAgeDNQbSA9IHN1bShYM1ApLA0KICAgICAgICAgICAgICB4M1BhID0gc3VtKFgzUEEpLA0KICAgICAgICAgICAgICB4M1AuID0gcm91bmQoeDNQbS94M1BhLCAzKSkgJT4lDQogICAgZmlsdGVyKHgzUG0gPj0gMjUwKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoeDNQLiksIGRlc2MoeDNQbSkpICU+JQ0KICAgIG11dGF0ZShSYW5rID0gbWluX3JhbmsoZGVzYyh4M1AuKSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpDQoNCkFsbFRpbWUzUC4gJT4lDQogICAgaGVhZChuPTEwKSAlPiUNCiAgICBtdXRhdGUoUG9zID0gY2VsbF9zcGVjKFBvcywgY29sb3IgPSAid2hpdGUiLCBhbGlnbiA9ICJjIiwgDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSBmYWN0b3IoUG9zLCBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc0NvbG9yQ29kZSkpKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJBbGwtVGltZSAzIFBvaW50cyBGaWVsZCBHb2FscyAlIExlYWRlcnMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBULCBjb2xvciA9ICJ5ZWxsb3ciLCBiYWNrZ3JvdW5kID0gIiNGRjAwMDAiKSAlPiUNCiAgICBjb2x1bW5fc3BlYygyLCBib2xkID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoMTEsIGJvbGQgPSBULCBjb2xvciA9ICJ3aGl0ZSIsIGJhY2tncm91bmQgPSAiIzc3Nzc3NyIpICU+JQ0KICAgIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIzMDBweCIpDQpgYGANCg0KPC9kaXY+DQoNCjxici8+DQoNCkxldCdzIGdvIHRvIHRoZSBwbG90Lg0KDQpgYGB7ciBmaWcud2lkdGggPSA5fQ0KTGFiZWxGaXggPC0gQWxsVGltZTNQLiAlPiUNCiAgICBoZWFkKDEwKSAlPiUNCiAgICBtdXRhdGUoUGxheWVyID0gZmN0X3Jlb3JkZXIoUGxheWVyLCBkZXNjKHgzUC4pLCBmdW49bWVhbiksDQogICAgICAgICAgIHgzUGEgPSB4M1BhLXgzUG0sDQogICAgICAgICAgIHgzUC4gPSBwYXN0ZSh4M1AuICogMTAwLCAiJSIpKSAlPiUNCiAgICBoZWFkKG49MTApICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCB4M1BtOngzUGEsIC1jKFBsYXllciwgeDNQLikpICU+JQ0KICAgIGZpbHRlcihQYXJhbWV0ZXIgPT0gIngzUGEiKQ0KICAgIA0KQWxsVGltZTNQLiAlPiUNCiAgICBoZWFkKDEwKSAlPiUNCiAgICBtdXRhdGUoUGxheWVyID0gZmN0X3Jlb3JkZXIoUGxheWVyLCBkZXNjKHgzUC4pLCBmdW49bWVhbiksDQogICAgICAgICAgIHgzUGEgPSB4M1BhLXgzUG0pICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCB4M1BtOngzUGEsIC1jKFBsYXllciwgeDNQLikpICU+JQ0KICAgIGdncGxvdChhZXMoUGxheWVyLCBDb3VudCwgZmlsbCA9IFBhcmFtZXRlcikpICsgDQogICAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ3N0YWNrJykgKw0KICAgIGdlb21fbGFiZWwoYWVzKGxhYmVsID0geDNQLiwNCiAgICAgICAgICAgICAgICAgICB5ID0gNTAwMCksDQogICAgICAgICAgICAgICBkYXRhID0gTGFiZWxGaXgsDQogICAgICAgICAgICAgICBzdGF0ID0gJ2lkZW50aXR5JywNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gInN0YWNrIiwNCiAgICAgICAgICAgICAgIGZpbGwgPSAiI0ZGNDAwMCIsDQogICAgICAgICAgICAgICBjb2xvciA9ICIjMDBGRkZGIiwNCiAgICAgICAgICAgICAgIHNpemUgPSA0LjUpICsNCiAgICBnZ3RpdGxlKCJBbGwtVGltZSAzIFBvaW50cyBQZXJjZW50YWdlIExlYWRlcnMiKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICAgIHhsYWIoIiIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHM9YygiM1B0cy1taXNzZWQgICIsICIzUHRzLW1hZGUiKSwgdmFsdWVzPWMoIiM2NkIyNjYiLCAiIzAwODAwMCIpKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikNCmBgYA0KDQo8YnIvPg0KDQpBbmQgYWdhaW4sIGhlcmUgY29tZXMgbXkgZmF2b3JpdGUgYm94ZXMsIHRoZSByZXN1bHQgb2YgY29tYmluaW5nIEhUTUwsIENTUyBhbmQgUiBjb2RlLg0KDQpgYGB7cn0NClRvcDNQLlBvcyA8LSBBbGxUaW1lM1AuICU+JQ0KICAgIGdyb3VwX2J5KFBvcykgJT4lDQogICAgc3VtbWFyaXNlKFBsYXllciA9IFBsYXllclt3aGljaC5tYXgoeDNQLildLA0KICAgICAgICAgICAgICBUZWFtID0gVGVhbVt3aGljaC5tYXgoeDNQLildLA0KICAgICAgICAgICAgICBBY3RpdmVZZWFycyA9IEFjdGl2ZVllYXJzW3doaWNoLm1heCh4M1AuKV0sDQogICAgICAgICAgICAgIEdhbWVzID0gR2FtZXNbd2hpY2gubWF4KHgzUC4pXSwNCiAgICAgICAgICAgICAgTVBHID0gTVBHW3doaWNoLm1heCh4M1AuKV0sDQogICAgICAgICAgICAgIFBQRyA9IFBQR1t3aGljaC5tYXgoeDNQLildLA0KICAgICAgICAgICAgICB4M1BtID0geDNQbVt3aGljaC5tYXgoeDNQLildLA0KICAgICAgICAgICAgICB4M1BhID0geDNQYVt3aGljaC5tYXgoeDNQLildLA0KICAgICAgICAgICAgICB4M1AuID0geDNQLlt3aGljaC5tYXgoeDNQLildKQ0KYGBgDQoNCg0KPGRpdiBpZD0iVG9wQnlQb3MiIGNsYXNzPSJjbGVhciI+DQo8aDE+QmVzdCAzLVBvaW50cyAlIGJ5IFBvc2l0aW9uPC9oMT4gIA0KPGRpdiBjbGFzcz0iVG9wUGxheWVyIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICNGRjAwMDA7dGV4dC1hbGlnbjogYzsiID5DPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cDovL2EuZXNwbmNkbi5jb20vY29tYmluZXIvaT9pbWc9L2kvaGVhZHNob3RzL25iYS9wbGF5ZXJzL2Z1bGwvMTAxNC5wbmcmdz0zNTAmaD0yNTQiIGFsdD0iIj48L2ltZz48L3NwYW4+PC9oMz4NCjxkaXYgY2xhc3M9IlBOYW1lIj48aDQ+PGJyLz5gciBUb3AzUC5Qb3MkUGxheWVyWzFdYDwvaDQ+PC9kaXY+DQo8ZGl2IGNsYXNzPSJQY3QiIGhyZWY9IiI+YHIgVG9wM1AuUG9zJHgzUC5bMV1gPC9kaXY+ICANCjx1bD4NCjxsaT48Yj5GRzogPC9iPmByIHBhc3RlKFRvcDNQLlBvcyR4M1BtWzFdLCAiLyIsIFRvcDNQLlBvcyR4M1BhWzFdKWA8L2xpPg0KPGxpPjxiPlBQRzogPC9iPmByIFRvcDNQLlBvcyRQUEdbMV1gPC9saT4NCjxsaT48Yj5ZZWFyczogPC9iPmByIFRvcDNQLlBvcyRBY3RpdmVZZWFyc1sxXWA8L2xpPgkNCjwvdWw+DQo8L2Rpdj4NCg0KPGRpdiBjbGFzcz0iVG9wUGxheWVyIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICNGRkE1MDA7dGV4dC1hbGlnbjogYzsiID5QRjwvc3Bhbj48YnIvPjxzcGFuPjxpbWcgY2xhc3M9InBob3RvIiBzcmM9Imh0dHA6Ly9hLmVzcG5jZG4uY29tL2NvbWJpbmVyL2k/aW1nPS9pL2hlYWRzaG90cy9uYmEvcGxheWVycy9mdWxsLzE5OTYucG5nJnc9MzUwJmg9MjU0IiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wM1AuUG9zJFBsYXllclsyXWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcDNQLlBvcyR4M1AuWzJdYDwvZGl2PiAgICAgICAgIA0KPHVsPg0KPGxpPjxiPkZHOiA8L2I+YHIgcGFzdGUoVG9wM1AuUG9zJHgzUG1bMl0sICIvIiwgVG9wM1AuUG9zJHgzUGFbMl0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wM1AuUG9zJFBQR1syXWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wM1AuUG9zJEFjdGl2ZVllYXJzWzJdYDwvbGk+CQkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiPg0KPGgzPjxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0REREQwMDt0ZXh0LWFsaWduOiBjOyIgPlNGPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cDovL2EuZXNwbmNkbi5jb20vY29tYmluZXIvaT9pbWc9L2kvaGVhZHNob3RzL25iYS9wbGF5ZXJzL2Z1bGwvMjAxMC5wbmcmdz0zNTAmaD0yNTQiIGFsdD0iIj48L2ltZz48L3NwYW4+PC9oMz4NCjxkaXYgY2xhc3M9IlBOYW1lIj48aDQ+PGJyLz5gciBUb3AzUC5Qb3MkUGxheWVyWzNdYDwvaDQ+PC9kaXY+DQo8ZGl2IGNsYXNzPSJQY3QiIGhyZWY9IiI+YHIgVG9wM1AuUG9zJHgzUC5bM11gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3AzUC5Qb3MkeDNQbVszXSwgIi8iLCBUb3AzUC5Qb3MkeDNQYVszXSlgPC9saT4NCjxsaT48Yj5QUEc6IDwvYj5gciBUb3AzUC5Qb3MkUFBHWzNdYDwvbGk+DQo8bGk+PGI+WWVhcnM6IDwvYj5gciBUb3AzUC5Qb3MkQWN0aXZlWWVhcnNbM11gPC9saT4JCQ0KPC91bD4gDQo8L2Rpdj4NCg0KPGRpdiBjbGFzcz0iVG9wUGxheWVyIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICMwMDAwRkY7dGV4dC1hbGlnbjogYzsiID5TRzwvc3Bhbj48YnIvPjxzcGFuPjxpbWcgY2xhc3M9InBob3RvIiBzcmM9Imh0dHBzOi8vY2RuLmhvb3BzaHlwZS5jb20vaS8zYi9hMy8zNy9odWJlcnQtZGF2aXMucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wM1AuUG9zJFBsYXllcls0XWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcDNQLlBvcyR4M1AuWzRdYDwvZGl2PiAgICAgICAgIA0KPHVsPg0KPGxpPjxiPkZHOiA8L2I+YHIgcGFzdGUoVG9wM1AuUG9zJHgzUG1bNF0sICIvIiwgVG9wM1AuUG9zJHgzUGFbNF0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wM1AuUG9zJFBQR1s0XWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wM1AuUG9zJEFjdGl2ZVllYXJzWzRdYDwvbGk+CQkNCjwvdWw+IA0KPC9kaXY+DQoNCjxkaXYgY2xhc3M9IlRvcFBsYXllciIgaWQ9IlRvcEFscGhhIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICMzMkNEMzI7dGV4dC1hbGlnbjogYzsiID5QRzwvc3Bhbj48YnIvPjxzcGFuPjxpbWcgY2xhc3M9InBob3RvIiBzcmM9Imh0dHBzOi8vY2RuLmhvb3BzaHlwZS5jb20vaS84Mi82MS8xMC9zdGV2ZS1rZXJyLnBuZyIgYWx0PSIiPjwvaW1nPjwvc3Bhbj48L2gzPg0KPGRpdiBjbGFzcz0iUE5hbWUiPjxoND48YnIvPmByIFRvcDNQLlBvcyRQbGF5ZXJbNV1gPC9oND48L2Rpdj4NCjxkaXYgY2xhc3M9IlBjdCIgaHJlZj0iIj5gciBUb3AzUC5Qb3MkeDNQLls1XWA8L2Rpdj4gICAgICAgICANCjx1bD4NCjxsaT48Yj5GRzogPC9iPmByIHBhc3RlKFRvcDNQLlBvcyR4M1BtWzVdLCAiLyIsIFRvcDNQLlBvcyR4M1BhWzVdKWA8L2xpPg0KPGxpPjxiPlBQRzogPC9iPmByIFRvcDNQLlBvcyRQUEdbNV1gPC9saT4NCjxsaT48Yj5ZZWFyczogPC9iPmByIFRvcDNQLlBvcyRBY3RpdmVZZWFyc1s1XWA8L2xpPgkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8L2Rpdj4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQojIyMgMy1Qb2ludHMgcGVyIDM2IE1pbnV0ZXMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTnVuYyBuZWMgcHVsdmluYXIgZXJvcy4gUHJvaW4gZWxlbWVudHVtIG51bmMgZWdldCBmcmluZ2lsbGEgZWxlaWZlbmQuIE51bGxhIGZhY2lsaXNpLiBWZXN0aWJ1bHVtIHZpdGFlIGxhY3VzIGV0IGZlbGlzIGZhY2lsaXNpcyBwaGFyZXRyYS4gSW4gaWQgZGlhbSBsYWN1cy4gRnVzY2UgZXQgcHVsdmluYXIgdXJuYSwgYWMgZ3JhdmlkYSBpcHN1bS4gSW50ZXJkdW0gZXQgbWFsZXN1YWRhIGZhbWVzIGFjIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzLiBWaXZhbXVzIHRyaXN0aXF1ZSB2aXZlcnJhIHN1c2NpcGl0LiBTdXNwZW5kaXNzZSBlbGVpZmVuZCBtb2xsaXMgZXVpc21vZC4gQWxpcXVhbSBzZWQgdHVycGlzIGlwc3VtLiBEb25lYyBldCBsaWJlcm8gc2l0IGFtZXQgbWkgdWx0cmljaWVzIHBvcnRhLg0KDQo8ZGl2IGNsYXNzPSJ0YWJsZSI+DQoNCmBgYHtyfQ0KQWxsVGltZTNQbWEgPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgZmlsdGVyKHN1bShHKSA+PSA0MDApICU+JQ0KICAgIHN1bW1hcmlzZShQb3MgPSBnZXRtb2RlKFBvc2l0aW9uKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBBY3RpdmVZZWFycyA9IHBhc3RlKGdldG1vZGUoWWVhclN0YXJ0KSwgIi0iLCBnZXRtb2RlKFllYXJFbmQpKSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBzdW0oRyksDQogICAgICAgICAgICAgIE1pbnV0ZXMgPSBzdW0oTVApLA0KICAgICAgICAgICAgICBNUEcgPSByb3VuZChNaW51dGVzIC9zdW0oRyksIDEpLA0KICAgICAgICAgICAgICBQUEcgPSByb3VuZChzdW0oUFRTKS9zdW0oRyksIDEpLA0KICAgICAgICAgICAgICB4M1BtXzM2ID0gcm91bmQoKHN1bShYM1ApIC8gTWludXRlcykgKiAzNiwgMSksDQogICAgICAgICAgICAgIHgzUG1fRyA9IHJvdW5kKHN1bShYM1ApIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICB4M1BhXzM2ID0gcm91bmQoKHN1bShYM1BBKSAvIE1pbnV0ZXMpICogMzYsIDEpLA0KICAgICAgICAgICAgICB4M1BhX0cgPSByb3VuZChzdW0oWDNQQSkgLyBHYW1lcywgMSksDQogICAgICAgICAgICAgIHgzUC4gPSByb3VuZChzdW0oWDNQKS9zdW0oWDNQQSksIDMpKSAlPiUNCiAgICBzZWxlY3QoLSJNaW51dGVzIikNCg0KQWxsVGltZTNQbWEgJT4lDQogICAgYXJyYW5nZShkZXNjKHgzUG1fMzYpLCBkZXNjKHgzUC4pKSAlPiUNCiAgICBoZWFkKG49MTApICU+JQ0KICAgIG11dGF0ZShSYW5rID0gbWluX3JhbmsoZGVzYyh4M1BtXzM2KSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBhbGlnbiA9ICJjIiwgY2FwdGlvbiA9ICIzLVBvaW50cyBNYWRlIHBlciAzNiBNaW51dGVzIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBULCBmb250X3NpemUgPSAxMSkgJT4lDQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFQsIGNvbG9yID0gInllbGxvdyIsIGJhY2tncm91bmQgPSAiI0ZGMDAwMCIpICU+JQ0KICAgIGNvbHVtbl9zcGVjKGMoMiwgMTMpLCBib2xkID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoOSwgYm9sZCA9IFQsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9ICIjNzc3Nzc3IikgJT4lDQogICAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikNCmBgYA0KPGJyLz4NCmBgYHtyfQ0KQWxsVGltZTNQbWEgJT4lDQogICAgYXJyYW5nZShkZXNjKHgzUGFfMzYpLCBkZXNjKHgzUC4pKSAlPiUNCiAgICBoZWFkKG49MTApICU+JQ0KICAgIG11dGF0ZShSYW5rID0gbWluX3JhbmsoZGVzYyh4M1BhXzM2KSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBhbGlnbiA9ICJjIiwgY2FwdGlvbiA9ICIzLVBvaW50cyBBdHRlbXB0cyBwZXIgMzYgTWludXRlcyIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gVCwgZm9udF9zaXplID0gMTEpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBULCBjb2xvciA9ICJ5ZWxsb3ciLCBiYWNrZ3JvdW5kID0gIiNGRjAwMDAiKSAlPiUNCiAgICBjb2x1bW5fc3BlYyhjKDIsIDEzKSwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDExLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCjwvZGl2Pg0KDQpgYGB7ciBmaWcud2lkdGg9OX0NCkFsbFRpbWUzUG1hICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh4M1BtXzM2KSwgZGVzYyh4M1AuKSkgJT4lDQogICAgbXV0YXRlKFBsYXllciA9IHJlb3JkZXIoUGxheWVyLCBkZXNjKHgzUG1fMzYpLCBGVU49bWVhbikpICU+JQ0KICAgIGhlYWQobj0xMCkgJT4lDQogICAgZ2dwbG90KGFlcyhncm91cCA9IDEpKSArDQogICAgZ2VvbV9zZWdtZW50KGFlcyh4PVBsYXllciwgeGVuZD1QbGF5ZXIsIHk9eDNQbV8zNiwgeWVuZD14M1BhXzM2KSwgY29sb3I9ImJsYWNrIikgKw0KICAgIGdlb21fcG9pbnQoYWVzKFBsYXllciwgeDNQbV8zNiwgY29sb3I9IiNGRjU4MDAiKSwgc2l6ZT01KSArDQogICAgZ2VvbV9wb2ludChhZXMoUGxheWVyLCB4M1BhXzM2LCBjb2xvcj0iI0ZGNTgwMCIpLCBzaXplPTIuNSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKFBsYXllciwgeDNQbV9HLCBjb2xvcj0iIzAwOWRmZiIpLCBzaXplPTUsIHNoYXBlPTE4KSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICAgIGdndGl0bGUoIjMtUHRzIE1hZGUgcGVyIDM2IE1pbnV0ZXMgdnMuIDMtUHRzIE1hZGUgcGVyIEdhbWUiKSArDQogICAgeGxhYigiIikgKw0KICAgIHlsYWIoIjMtUHRzIE1hZGUiKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsIHZhbHVlcz1jKCIjMDA5ZGZmIiwgIiNGRjU4MDAiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjMtUHRzIGEvbSBwZXIgR2FtZSIsICIzLVB0cyBhL20gcGVyIDM2IG1pbnV0ZXMiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQocmV2ZXJzZT1UUlVFKSkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpDQpgYGANCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQojIyMgQklHIDMtUG9pbnRlcnMgTmFtZXMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KVGhlIFRocmVlLVBvaW50ZXJzIHdpbGwgYmUgc29ydGVkIGFjY29yZGluZyB0byB0aGUgZm9sbG93aW5nIGFyYnJpdGFyeSBjcml0ZXJpYSA6DQoNCiogQW5udWFsIGxlYWd1ZSBsZWFkZXIgaW4gMy1Qb2ludHMgcGVyY2VudGFnZSBjYXRlZ29yeSBlYXJuIDEgcG9pbnQuDQoqIFRvcCAxMCBhbGwtdGltZSBsZWFkZXJzIGluIDMtUG9pbnRzIHBlcmNlbnRhZ2UgY2F0ZWdvcnkgZWFybiAyIHBvaW50cy4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gMy1Qb2ludHMgbWFkZSBjYXRlZ29yeSB3aXRoIDNQJSBtb3JlIHRoYW4gMzglIGVhcm4gMSBwb2ludC4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gMy1Qb2ludHMgbWFkZSBwZXIgMzYgbWludXRlcyBjYXRlZ29yeSB3aXRoIDNQJSBtb3JlIHRoYW4gMzglIGVhcm4gMSBwb2ludC4NCiogQW5udWFsIFRocmVlLVBvaW50cyBDb250ZXN0IHdpbm5lciBlYXJuIDEgcG9pbnQuDQoNCkNhbGN1bGF0ZSB0aGUgcG9pbnRzIGFuZCB3ZSBoYXZlIHRoZSByYW5rIQ0KDQpOb3RlIHRoYXQgd2UgaGF2ZSBzcGVjaWFsIGNhdGVnb3J5IGhlcmUgdGhhdCBjYW5ub3QgYmUgYXBwbHkgYW55d2hlcmUgZWxzLCAqKiJUaHJlZS1Qb2ludHMgQ29udGVzdCB3aW5uZXIiKiouIEl0J3MgYW5udWFsIGNvbnRlc3QgaGVsZCBkdXJpbmcgTkJBIEFsbC1TdGFyIFdlZWtlbmQuIEkgaGFkIHRvIGFkZCB0aGUgd2lubmVycyBkYXRhIG1hbnVhbGx5Lg0KDQo8YnIvPg0KDQpMZXQgdGhlIGNhbGN1bGF0aW9uIGJlZ2luLg0KDQo8ZGl2IGNsYXNzPSJ0YWJsZSI+DQpgYGB7cn0NCiMgRmlsdGVyIHRvdGFsIDMtUG9pbnRzIG1hZGUgd2l0aCAzUCUgPiAuMzgwDQpBbGxUaW1lM1BtIDwtIE5CQSAlPiUNCiAgICBncm91cF9ieShQbGF5ZXIpICU+JQ0KICAgIHN1bW1hcmlzZSh4M1BtID0gc3VtKFgzUCksDQogICAgICAgICAgICAgIHgzUGEgPSBzdW0oWDNQQSksDQogICAgICAgICAgICAgIHgzUC4gPSB4M1BtIC8geDNQYSkgJT4lDQogICAgZmlsdGVyKHgzUC4gPiAwLjM4KSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoeDNQbSkpICU+JSBoZWFkKDEwKQ0KDQojIEZpbHRlciAzLVBvaW50cyBtYWRlIHBlciAzNiBtaW51dGVzIHdpdGggM1AlID4gLjM4MA0KQWxsVGltZTNQbTM2IDwtIEFsbFRpbWUzUG1hICU+JQ0KICAgIGZpbHRlcih4M1AuID49IDAuMzgpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh4M1BtXzM2KSkgJT4lDQogICAgaGVhZChuPTEwKQ0KDQojIENyZWF0ZSBUaHJlZS1Qb2ludHMgQ29udGVzdCBkYXRhIGZyYW1lDQpZZWFyIDwtIGMoMTk4NjoxOTk4LCAyMDAwOjIwMTgpDQpQbGF5ZXIgPC0gYyhyZXAoIkxhcnJ5IEJpcmQiLCAzKSwgIkRhbGUgRWxsaXMiLCByZXAoIkNyYWlnIEhvZGdlcyIsIDMpLCByZXAoIk1hcmsgUHJpY2UiLCAyKSwgIkdsZW4gUmljZSIsICJUaW0gTGVnbGVyIiwgIlN0ZXZlIEtlcnIiLCByZXAoIkplZmYgSG9ybmFjZWsiLCAyKSwgIlJheSBBbGxlbiIsIHJlcCgiUGVqYSBTdG9qYWtvdmljIiwgMiksICJWb3Nob24gTGVuYXJkIiwgIlF1ZW50aW4gUmljaGFyZHNvbiIsIkRpcmsgTm93aXR6a2kiLCByZXAoIkphc29uIEthcG9ubyIsIDIpLCAiRGFlcXVhbiBDb29rIiwgIlBhdWwgUGllcmNlIiwgIkphbWVzIEpvbmVzIiwgIktldmluIExvdmUiLCAiS3lyaWUgSXJ2aW5nIiwgIk1hcmNvIEJlbGluZWxsaSIsICJTdGVwaGVuIEN1cnJ5IiwgIktsYXkgVGhvbXBzb24iLCAiRXJpYyBHb3Jkb24iLCAiRGV2aW4gQm9va2VyIikNClRQQ29udGVzdCA8LSBkYXRhLmZyYW1lKFllYXIsIFBsYXllcikNCg0KIyBDcmVhdGUgYWxsIHRocmVlLXBvaW50ZXJzIGxlYWRlcnMNClN1cGVyM1BvaW50ZXJzIDwtIGRhdGEuZnJhbWUoUGxheWVyID0gYyhhcy5jaGFyYWN0ZXIoVG9wM1B0cy4kUGxheWVyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocmVwKGhlYWQoQWxsVGltZTNQLiRQbGF5ZXIsIDEwKSwgMikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihBbGxUaW1lM1BtJFBsYXllciksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKEFsbFRpbWUzUG0zNiRQbGF5ZXIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihUUENvbnRlc3QkUGxheWVyKSkpICU+JQ0KICAgIGNvdW50KFBsYXllcikgJT4lDQogICAgYXJyYW5nZShkZXNjKG4pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHRhYmxlOg0KICAgIA0KU3VwZXIzUG9pbnRlcnMgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBtdXRhdGUoUG9zID0gZ2V0bW9kZShOQkEkUG9zaXRpb25bTkJBJFBsYXllciAlaW4lIFBsYXllcl0pLA0KICAgICAgICAgICB4M1BtID0gc3VtKE5CQSRYM1BbTkJBJFBsYXllciAlaW4lIFBsYXllcl0sIG5hLnJtID0gVCksDQogICAgICAgICAgIHgzUGEgPSBzdW0oTkJBJFgzUEFbTkJBJFBsYXllciAlaW4lIFBsYXllcl0sIG5hLnJtID0gVCksDQogICAgICAgICAgICIzUCUiID0gcm91bmQoeDNQbS94M1BhLCAzKSwNCiAgICAgICAgICAgVG9rZW5zID0gcGFzdGUoc3RycmVwKCJ8IiwgbikpLA0KICAgICAgICAgICBUb3RhbFRva2VucyA9IG4pICU+JQ0KICAgIHNlbGVjdCgtIm4iKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJUaGUgTW9zdCBQcm9taW5lbnQgVGhyZWUtUG9pbnRlcnMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAiY2VudGVyIikgJT4lDQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDYsIGJvbGQgPSBULCBjb2xvciA9ICJnb2xkIikgJT4lDQogICAgY29sdW1uX3NwZWMoNywgYm9sZCA9IFQsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9ICIjNzc3Nzc3IikgJT4lDQogICAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikNCmBgYA0KDQo8L2Rpdj4NCk5vdyBzZWUgdGhlaXIgbmFtZSBjbG91ZA0KDQpgYGB7ciBmaWcud2lkdGggPSA4Ljh9DQpTdXBlcjNQb2ludGVycyAlPiUgd29yZGNsb3VkMihzaXplPTAuNSwgY29sb3I9J3JhbmRvbS1saWdodCcsIGJhY2tncm91bmRDb2xvcj0iYmxhY2siKQ0KYGBgDQoNCjxici8+DQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyBBbGwgMy1Qb2ludCBTaG9vdGVycyBDYXJlZXIgUGVyZm9ybWFuY2UNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KSSBjYW4ndCByZXNpc3QgdG8gc2VlIGhvdyBhbGwgdGhlIHRocmVlLXBvaW50ZXJzIGNvbXBhcmVzIHllYXItYnkteWVhciwgc28gbGV0J3Mgc2VlIHRoZWlyIGNhcmVlciBwZXJmb3JtYW5jZS4NCg0KYGBge3IgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9OX0NCk5CQSAlPiUNCiAgICBmaWx0ZXIoUGxheWVyICVpbiUgU3VwZXIzUG9pbnRlcnMkUGxheWVyKSAlPiUNCiAgICBncm91cF9ieShBZ2UsIFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKHgzUG0gPSBzdW0oWDNQKSwNCiAgICAgICAgICAgICAgeDNQYSA9IHN1bShYM1BBKSwNCiAgICAgICAgICAgICAgeDNQLiA9IHgzUG0veDNQYSkgJT4lDQogICAgZmlsdGVyKHgzUC4gPCAwLjcpICU+JQ0KICAgIGdncGxvdChhZXMoQWdlLCBQbGF5ZXIsIGZpbGw9eDNQLikpICsNCiAgICBnZW9tX3RpbGUoY29sb3IgPSAiZ3JleTUwIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gYnJld2VyLnBhbCg5LCAiUmVkcyIpKSArDQogICAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhQbGF5ZXIpKSkgKw0KICAgIGdndGl0bGUoIkFsbCAzLVBvaW50IFNob290ZXJzIENhcmVlciBQZXJmb3JtYW5jZSIpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQpgYGANCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQoNCiMjIyAzLVBvaW50IFJhdGlvDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCkRvbid0IHlvdSBkYXJlIHRvIHRoaW5rIHRoYXQgSSdtIGRvbmUgd2l0aCB0aGUgdGhyZWUtcG9pbnRzLCBpdCdzIHRoZSBtb3N0IGludGVyZXN0aW5nIHN1YmplY3Qgb2YgYWxsLiBTbyBsaWtlIGl0IG9yIG5vdCwgSSdtIGdvbm5hIHNlZSB0aGUgaGlnaGVzdCAzLXBvaW50IHJhdGlvLiBJdCBtZWFzdXJlIGhvdyBtYW55IHBlcmNlbnQgb2YgcG9pbnRzIGNvbWUgZnJvbSB0aGUgYmVoaW5kIHRoZSAzLXBvaW50cyBsaW5lLg0KDQpgYGB7ciBmaWcud2lkdGg9OX0NCk5CQSAlPiUNCiAgICBmaWx0ZXIoWWVhciA+PSAxOTgwICYgWDNQID4gMSkgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoUG9zID0gZ2V0bW9kZShQb3NpdGlvbiksDQogICAgICAgICAgICAgIFRlYW0gPSBnZXRtb2RlKFRtKSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBzdW0oRyksDQogICAgICAgICAgICAgIFB0czIgPSBzdW0oWDJQKSAqIDIsDQogICAgICAgICAgICAgIFB0czMgPSBzdW0oWDNQKSAqIDMsDQogICAgICAgICAgICAgIFB0c0ZUID0gc3VtKEZUKSwNCiAgICAgICAgICAgICAgUFRTID0gc3VtKFBUUyksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKFBUUyAvIEdhbWVzLCAxKSwNCiAgICAgICAgICAgICAgUHRzMnIgPSByb3VuZChQdHMyIC8gUFRTLCAzKSwNCiAgICAgICAgICAgICAgUHRzM3IgPSByb3VuZChQdHMzIC8gUFRTLCAzKSwNCiAgICAgICAgICAgICAgUHRzRlRyID0gcm91bmQoUHRzRlQgLyBQVFMsIDMpLA0KICAgICAgICAgICAgICB4M1BtID0gc3VtKFgzUCksDQogICAgICAgICAgICAgIHgzUGEgPSBzdW0oWDNQQSksDQogICAgICAgICAgICAgIHgzUC4gPSByb3VuZCh4M1BtIC8geDNQYSwgMykpICU+JQ0KICAgIGZpbHRlcih4M1BtID4gMjUwKSAlPiUNCiAgICBzZWxlY3QoLWMoeDNQbSwgeDNQYSwgUHRzMjpQdHNGVCkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhQdHMzcikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSByZW9yZGVyKFBsYXllciwgUHRzM3IsIEZVTj1tZWFuKSkgJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgVmFsdWUsIFB0czJyOlB0c0ZUcikgJT4lDQogICAgZ2dwbG90KGFlcyhQbGF5ZXIsIFZhbHVlLCBmaWxsPWZvcmNhdHM6OmZjdF9yZXYoUGFyYW1ldGVyKSkpICsNCiAgICBnZW9tX2Jhciggc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIpICsNCiAgICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IHBhc3RlKFZhbHVlICogMTAwLCAiJSIpKSwNCiAgICAgICAgICAgICAgIHN0YXQgPSAnaWRlbnRpdHknLA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksDQogICAgICAgICAgICAgICBmaWxsID0gIiNGRjQwMDAiLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiIzAwRkZGRiIsDQogICAgICAgICAgICAgICBzaXplID0gMi41KSArDQogICAgZ2d0aXRsZSgiSGlnaGVzdCAzLVBvaW50cyBSYXRpbyIpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIHhsYWIoIiIpICsNCiAgICB5bGFiKCJTaG9vdGluZyIpICsNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCjxici8+DQoNClN0aWxsIG5vdCBzYXRpc2ZpZWQgZW5vdWdoLCBJJ20gZ29pbmcgdG8gc2VlIHNhbWUgcGxvdCBidXQgZmlsbGVkIHdpdGggdGhlIHRvcCBkb3plbiBiZXN0IHRocmVlLXBvaW50ZXJzIGluIHRoZSBsZWFndWUsIG1pbnVzIHRoZSBuYW1lIHRoYXQgYWxyZWFkeSBhcHBlYXIgaW4gdGhlIHBsb3QgYWJvdmUuDQoNCmBgYHtyIGZpZy53aWR0aCA9IDl9DQpOQkEgJT4lDQogICAgZmlsdGVyKFllYXIgPj0gMTk4MCAmIFBsYXllciAlaW4lIGhlYWQoU3VwZXIzUG9pbnRlcnMkUGxheWVyLCAxMikpICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHKSwNCiAgICAgICAgICAgICAgUHRzMiA9IHN1bShYMlApICogMiwNCiAgICAgICAgICAgICAgUHRzMyA9IHN1bShYM1ApICogMywNCiAgICAgICAgICAgICAgUHRzRlQgPSBzdW0oRlQpLA0KICAgICAgICAgICAgICBQVFMgPSBzdW0oUFRTKSwNCiAgICAgICAgICAgICAgUFBHID0gcm91bmQoUFRTIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBQdHMyciA9IHJvdW5kKFB0czIgLyBQVFMsIDMpLA0KICAgICAgICAgICAgICBQdHMzciA9IHJvdW5kKFB0czMgLyBQVFMsIDMpLA0KICAgICAgICAgICAgICBQdHNGVHIgPSByb3VuZChQdHNGVCAvIFBUUywgMyksDQogICAgICAgICAgICAgIHgzUG0gPSBzdW0oWDNQKSwNCiAgICAgICAgICAgICAgeDNQYSA9IHN1bShYM1BBKSwNCiAgICAgICAgICAgICAgeDNQLiA9IHJvdW5kKHgzUG0gLyB4M1BhLCAzKSkgJT4lDQogICAgZmlsdGVyKHgzUG0gPiAyNTAsIFBsYXllciAhPSBjKCJTdGV2ZSBOb3ZhayIsICJLeWxlIEtvcnZlciIpKSAlPiUNCiAgICBzZWxlY3QoLWMoeDNQbSwgeDNQYSwgUHRzMjpQdHNGVCkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhQdHMzcikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSBjKCJMYXJyeSBCaXJkIiwgIkIuSi4gQXJtc3Ryb25nIiwgIkRhbGUgRWxsaXMiLCAiQ3JhaWcgSG9kZ2VzIiwgIlRpbSBMZWdsZXIiLCAiUGVqYSBTdG9qYWtvdmljIiwgIkh1YmVydCBEYXZpcyIsICJTdGV2ZSBLZXJyIiwgIkphc29uIEthcG9ubyIsICJTdGVwaGVuIEN1cnJ5IikpKSAlPiUNCiAgICBnYXRoZXIoUGFyYW1ldGVyLCBWYWx1ZSwgUHRzMnI6UHRzRlRyKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKFBsYXllciwgVmFsdWUsIGZpbGw9Zm9yY2F0czo6ZmN0X3JldihQYXJhbWV0ZXIpKSkgKw0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIikgKw0KICAgIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gcGFzdGUoVmFsdWUgKiAxMDAsICIlIikpLA0KICAgICAgICAgICAgICAgc3RhdCA9ICdpZGVudGl0eScsDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgIGZpbGwgPSAiI0ZGNDAwMCIsDQogICAgICAgICAgICAgICBjb2xvciA9ICIjMDBGRkZGIiwNCiAgICAgICAgICAgICAgIHNpemUgPSAyLjUpICsNCiAgICBnZ3RpdGxlKCJTaG9vdGluZyBSYXRpbyBvZiB0aGUgU3VwZXIzUG9pbnRlcnMiKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICB4bGFiKCIiKSArDQogICAgeWxhYigiU2hvb3RpbmciKSArDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpJdCdzIGludGVyZXN0aW5nIHRvIHNlZSB0aGF0LCBkZXNwaXRlIGJlaW5nIGEgdGhyZWUgY29uc2VjdXRpdmUgdGhyZWUtcG9pbnQgY29udGVzdCB3aW5uZXIsIGFuZCB0aG9zZSBhbWF6aW5nIHRocmVlcyB3ZSBzYXcgaW4gdGhlIE5CQSBoaWdobGlnaHQsIExhcnJ5IEJpcmQgYWN0dWFsbHkgYWNxdWlyZWQgbGVzcyB0aGFuIG9uZS10ZW50aCBvZiBoaXMgcG9pbnRzIGZyb20gYmVoaW5kIHRoZSB0aHJlZS1wb2ludCBsaW5lLg0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyAzLVBvaW50IFJhdGUNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KV2hpbGUgaW4gdGhlIDMtcG9pbnQgcmF0aW8gd2UgY2FuIHNlZSBob3cgbWFueSBwZXJjZW50IG9mIHBvaW50cyBlYXJuZWQgZnJvbSAzLXBvaW50LCBpbiAzLXBvaW50IHJhdGUgd2UgY2FuIHNlZSBwbGF5ZXJzIHdobyBsb3ZlcyB0byB0YWtlIGxvbmctcmFuZ2Ugc2hvb3RpbmcgYXR0ZW1wdHMgbW9yZSB0aGFuIHNob3J0ZXIgZGlzdGFuY2UuDQoNCmBgYHtyIGZpZy53aWR0aCA9IDksIHdhcm5pbmc9RkFMU0V9DQp4M1ByYXRlQVZHIDwtIE5CQSAlPiUNCiAgICBzdW1tYXJpc2UoUGxheWVyID0gIkF2ZXJhZ2UiLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGR2EgPSBzdW0oRkdBLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICB4M1BhID0gc3VtKFgzUEEsIG5hLnJtID0gVCksDQogICAgICAgICAgICAgIEZHYXBHID0gcm91bmQoRkdhIC8gR2FtZXMsIDIpLA0KICAgICAgICAgICAgICB4M1BhcEcgPSByb3VuZCh4M1BhIC8gR2FtZXMsIDIpLA0KICAgICAgICAgICAgICB4M1ByID0gcm91bmQoeDNQYSAvIEZHYSwgMykpICU+JQ0KICAgIHNlbGVjdCgtYyhHYW1lcywgeDNQYSwgRkdhKSkNCg0KeDNQcmF0ZSA8LSBOQkEgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoUG9zID0gZ2V0bW9kZShQb3MpLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBQVFMgPSBzdW0oUFRTKSwNCiAgICAgICAgICAgICAgUFBHID0gcm91bmQoUFRTIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBGR2EgPSBzdW0oRkdBKSwNCiAgICAgICAgICAgICAgeDNQYSA9IHN1bShYM1BBKSwNCiAgICAgICAgICAgICAgeDNQciA9IHJvdW5kKHgzUGEgLyBGR2EsIDMpLA0KICAgICAgICAgICAgICB4M1BhcEcgPSByb3VuZCh4M1BhIC8gR2FtZXMsIDIpLA0KICAgICAgICAgICAgICBGR2FwRyA9IHJvdW5kKEZHYSAvIEdhbWVzLCAyKSkgJT4lDQogICAgZmlsdGVyKHgzUGEgPiAyNTApICU+JQ0KICAgIG11dGF0ZShSYW5rID0gbWluX3JhbmsoZGVzYyh4M1ByKSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGFycmFuZ2UoUmFuaykgJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgYmluZF9yb3dzKHgzUHJhdGVBVkcpDQoNCkxhYmVsRml4IDwtIHgzUHJhdGUgJT4lDQogICAgbXV0YXRlKHgzUGFwRyA9IHgzUGFwRywNCiAgICAgICAgICAgeDNQciA9IHBhc3RlKHgzUHIgKiAxMDAsICIlIikpICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCB4M1BhcEcsIC1jKFBsYXllciwgeDNQcikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSB1bmlxdWUoUGxheWVyW29yZGVyKGRlc2MoeDNQcikpXSkpKQ0KDQp4M1ByYXRlICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCBGR2FwRzp4M1BhcEcsIC1jKFBsYXllciwgeDNQcikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSB1bmlxdWUoUGxheWVyW29yZGVyKGRlc2MoeDNQcikpXSkpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKFBsYXllciwgQ291bnQsIGZpbGwgPSBQYXJhbWV0ZXIsIGxhYmVsID0geDNQcikpICsNCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsNCiAgICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IHgzUHIsIHkgPSAwLjcpLA0KICAgICAgICAgICAgICBkYXRhID0gTGFiZWxGaXgsDQogICAgICAgICAgICAgIHN0YXQgPSAnaWRlbnRpdHknLA0KICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJzdGFjayIsDQogICAgICAgICAgICAgIGZpbGwgPSAiI0ZGRkZGRiIsDQogICAgICAgICAgICAgIGNvbG9yID0gIiMwMDAwMDAiLA0KICAgICAgICAgICAgICBzaXplID0gNCkgKw0KICAgIGdndGl0bGUoIjMtUG9pbnQgUmF0ZSIpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgeGxhYigiIikgKw0KICAgIHlsYWIoIjNQL0ZHIHBlciBnYW1lIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsIHZhbHVlcz1jKCIjMDAwMEZGIiwgIiMwMDgwMDAiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkZHIHBlciBHYW1lIiwgIjNQIHBlciBHYW1lIikpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQogICAgDQpgYGANCg0KTm93IEknbSBmaW5hbGx5IHNhdGlzZmllZCBoYXZpbmcgc2VlbiBldmVyeXRoaW5nIEkgd2FudGVkIHRvIHNlZSAoc28gZmFyKSwgbm93IHdlIG1hcmNoaW5nIGZvcndhcmQgdG8gc2VlIHRoZSBuZXh0IGNhdGVnb3J5Lg0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCi0tLQ0KDQo8YnIvPg0KDQojIyBNaXhlZA0KDQo8YnIvPg0KDQoNCiMjIyBFZmZlY3RpdmUgRmllbGQgR29hbCBQZXJjZW50YWdlDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCmBlRkcuYCAoKipFZmZlY3RpdmUgRmllbGQgR29hbCBQZXJjZW50YWdlKiopOiBUaGlzIHN0YXRpc3RpYyBhZGp1c3RzIGZvciB0aGUgZmFjdCB0aGF0IGEgMy1wb2ludCBmaWVsZCBnb2FsIGlzIHdvcnRoIG9uZSBtb3JlIHBvaW50IHRoYW4gYSAyLXBvaW50IGZpZWxkIGdvYWwuIEZvciBleGFtcGxlLCBzdXBwb3NlIFBsYXllciBBIGdvZXMgNCBmb3IgMTAgd2l0aCAyIHRocmVlcywgd2hpbGUgUGxheWVyIEIgZ29lcyA1IGZvciAxMCB3aXRoIDAgdGhyZWVzLiBFYWNoIHBsYXllciB3b3VsZCBoYXZlIDEwIHBvaW50cyBmcm9tIGZpZWxkIGdvYWxzLCBhbmQgdGh1cyB3b3VsZCBoYXZlIHRoZSBzYW1lIGVmZmVjdGl2ZSBmaWVsZCBnb2FsIHBlcmNlbnRhZ2UgKDUwJSkuDQoNClRoZSBmb3JtdWxhIGlzICRcZnJhY3tGRyArIDAuNSAqIDNQfXtGR0F9JA0KDQo8YnIvPg0KDQpDcmVhdGluZyB0YWJsZToNCg0KYGBge3J9DQpOQkEgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoUG9zID0gZ2V0bW9kZShQb3MpLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEFjdGl2ZVllYXJzID0gcGFzdGUoZ2V0bW9kZShZZWFyU3RhcnQpLCAiLSIsIGdldG1vZGUoWWVhckVuZCkpLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHKSwNCiAgICAgICAgICAgICAgUFRTID0gc3VtKFBUUyksDQogICAgICAgICAgICAgIHgyUG0gPSBzdW0oWDJQKSwNCiAgICAgICAgICAgICAgeDJQYSA9IHN1bShYMlBBKSwNCiAgICAgICAgICAgICAgRkdtID0gc3VtKEZHKSwNCiAgICAgICAgICAgICAgRkdhID0gc3VtKEZHQSksDQogICAgICAgICAgICAgIHgzUG0gPSBzdW0oWDNQLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICB4M1BhID0gc3VtKFgzUEEsIG5hLnJtID0gVCksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKFBUUy9HYW1lcywgMSksDQogICAgICAgICAgICAgIHgyUC4gPSByb3VuZCh4MlBtL3gyUGEsIDMpLA0KICAgICAgICAgICAgICB4M1AuID0gcm91bmQoeDNQbS94M1BhLCAzKSwNCiAgICAgICAgICAgICAgZUZHLiA9IHJvdW5kKChGR20gKyAwLjUgKiB4M1BtKSAvIEZHYSwgMykpICU+JQ0KICAgIGZpbHRlcihHYW1lcyA+IDQwMCkgJT4lDQogICAgc2VsZWN0KC1jKFBUUzp4M1BhKSkgJT4lDQogICAgYXJyYW5nZShkZXNjKGVGRy4pKSAlPiUNCiAgICBoZWFkKDIwKSAlPiUNCiAgICBtdXRhdGUoUmFuayA9IG1pbl9yYW5rKGRlc2MoZUZHLikpKSAlPiUNCiAgICBzZWxlY3QoUmFuaywgZXZlcnl0aGluZygpKSAlPiUNCiAgICBtdXRhdGUoUG9zID0gY2VsbF9zcGVjKFBvcywgY29sb3IgPSAid2hpdGUiLCBhbGlnbiA9ICJjIiwgDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSBmYWN0b3IoUG9zLCBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc0NvbG9yQ29kZSkpKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJFZmZlY3RpdmUgRmllbGQgR29hbCBQZXJjZW50YWdlIExlYWRlcnMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBULCBjb2xvciA9ICJ5ZWxsb3ciLCBiYWNrZ3JvdW5kID0gIiNGRjAwMDAiKSAlPiUNCiAgICBjb2x1bW5fc3BlYygyLCBib2xkID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoMTAsIGJvbGQgPSBULCBjb2xvciA9ICJ3aGl0ZSIsIGJhY2tncm91bmQgPSAiIzc3Nzc3NyIpICU+JQ0KICAgIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIzMDBweCIpDQpgYGANCg0KPGJyLz4NCg0KYGBge3IgZmlnLndpZHRoPTksIHdhcm5pbmc9RkFMU0V9DQplRkdMZWFkZXJzIDwtIE5CQSAlPiUNCiAgICBncm91cF9ieShQbGF5ZXIpICU+JQ0KICAgIHN1bW1hcmlzZShHYW1lcyA9IHN1bShHKSwNCiAgICAgICAgICAgICAgeDJQeCA9IHJvdW5kKHN1bShYMlApIC8gc3VtKEZHQSksIDMpLA0KICAgICAgICAgICAgICB4M1B4ID0gcm91bmQoKHN1bShYM1AsIG5hLnJtID0gVCkgKiAxLjUpIC8gc3VtKEZHQSksIDMpLA0KICAgICAgICAgICAgICBGR2F4ID0gcm91bmQoKHN1bShGR0EpIC0gKHN1bShYMlApICsgKHN1bShYM1AsIG5hLnJtID0gVCkgKiAxLjUpKSkgLyBzdW0oRkdBKSwgMyksDQogICAgICAgICAgICAgIGVGRy4gPSByb3VuZCgoc3VtKFgyUCkgKyBzdW0oWDNQLCBuYS5ybSA9IFQpICogMS41KSAvIHN1bShGR0EpLCAzKSkgJT4lDQogICAgZmlsdGVyKEdhbWVzID4gNDAwKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoZUZHLikpICU+JQ0KICAgIGhlYWQoMjApICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSB1bmlxdWUoUGxheWVyW29yZGVyKGRlc2MoZUZHLikpXSkpKSAlPiUNCiAgICBnYXRoZXIoUGFyYW1ldGVyLCBWYWx1ZSwgeDJQeDpGR2F4LCAtYyhHYW1lcykpDQoNCkxhYmVsRml4MSA8LSBlRkdMZWFkZXJzICU+JQ0KICAgIHNwcmVhZChQYXJhbWV0ZXIsIFZhbHVlKSAlPiUNCiAgICBtdXRhdGUoZUZHLiA9IHBhc3RlKGVGRy4gKiAxMDAsICIlIikpDQoNCmVGR0xlYWRlcnMgJT4lDQogICAgZ2dwbG90KGFlcyhQbGF5ZXIsIFZhbHVlLCBmaWxsID0gUGFyYW1ldGVyKSkgKyANCiAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBlRkcuLCB5ID0gMC45NSksIHNpemUgPSAzKSArDQogICAgZ2d0aXRsZSgiRWZmZWN0aXZlIEZpZWxkIEdvYWwgUGVyY2VudGFnZSIpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgeGxhYigiIikgKw0KICAgIHlsYWIoIlBlcmNlbnRhZ2UiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzPWMoIk1pc3NlZCAgIiwgIjJQICAiLCAiM1AiKSwgdmFsdWVzPWMoIiMwMDgwRkYiLCAiIzAwMDBGRiIsICIjMDA4MDAwIikpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KYGBgDQoNCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KIyMjIFNoYXJwZXN0IFNob290ZXJzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNClN0aWxsIG9ic2Vzc2luZyB3aXRoIHNob290ZXJzLCBJJ2QgbGlrZSB0byBmaW5kIHRoZSBzaGFycGVzdCBzaG9vdGVyKHMpIGluIHRoZSBsZWFndWUuIFRvIGFjaGlldmUgdGhpcywgSSBuZWVkIHRvIG5lZ2F0ZSBkdW5rcywgbGF5LXVwcywgZXZlbiBob29rcy4gSSBvbmx5IGludGVyZXN0ZWQgaW4gbWVkaXVtIHRvIGxvbmcgcmFuZ2Ugc2hvb3RlcnMgZm9yIHRoaXMgcHVycG9zZS4gVGhlcmVmb3JlLCBJIHNpbXBseSBkaXNtaXNzIEZHIGFuZCB0YWtlIGF2ZXJhZ2Ugb2YgYm90aCAzLXBvaW50ICUgYW5kIEZyZWUgVGhyb3cgJSBhcyBhIG1lYXN1cmVtZW50IHZhcmlhYmxlLg0KDQpgYGB7cn0NClNoYXJwZXN0U2hvb3RlcnMgPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgeDNQbSA9IHN1bShYM1AsIG5hLnJtID0gVCksDQogICAgICAgICAgICAgIHgzUGEgPSBzdW0oWDNQQSwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgICAgRlRtID0gc3VtKEZULCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICB4M1AuID0gcm91bmQoeDNQbS94M1BhLCAzKSwNCiAgICAgICAgICAgICAgRlQuID0gcm91bmQoRlRtL0ZUYSwgMyksDQogICAgICAgICAgICAgIFNob290LiA9IHJvdW5kKCh4M1AuICsgRlQuKS8yLCAzKSkgJT4lDQogICAgZmlsdGVyKHgzUGEgPiAyNTAgJiBGVGEgPiAxMDAwICYgRlQuID4gMC44NSAmIHgzUC4gPiAwLjM4KSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoU2hvb3QuKSkgJT4lDQogICAgbXV0YXRlKFJhbmsgPSBtaW5fcmFuayhkZXNjKFNob290LikpKSAlPiUNCiAgICBzZWxlY3QoUmFuaywgZXZlcnl0aGluZygpLCAtYyh4M1BtOkZUYSkpICU+JQ0KICAgIGhlYWQoMjApICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSB1bmlxdWUoUGxheWVyW29yZGVyKGRlc2MoU2hvb3QuKSldKSkpDQoNClNoYXJwZXN0U2hvb3RlcnMgJT4lDQogICAgbXV0YXRlKFBvcyA9IGNlbGxfc3BlYyhQb3MsIGNvbG9yID0gIndoaXRlIiwgYWxpZ24gPSAiYyIsIA0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gZmFjdG9yKFBvcywgYygiQyIsICJQRiIsICJTRiIsICJTRyIsICJQRyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3NDb2xvckNvZGUpKSkgJT4lDQogICAga2FibGUoZXNjYXBlID0gRkFMU0UsIGNhcHRpb24gPSAiU2hhcnBlc3QgU2hvb3RlcnMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImZsb2F0X2xlZnQiKSAlPiUNCiAgICBjb2x1bW5fc3BlYygyLCBib2xkID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFQsIGNvbG9yID0gInllbGxvdyIsIGJhY2tncm91bmQgPSAiI0ZGMDAwMCIpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDYsIGJvbGQgPSBULCBjb2xvciA9ICJ3aGl0ZSIsIGJhY2tncm91bmQgPSAiIzc3Nzc3NyIpDQpgYGANCg0KPGltZyBpZD0iU3RlcGhTaG9vdCIgc3JjPSJodHRwczovL2ltYWdlLmliYi5jby9mOWtpdFYvU3RlcGgtQ3VycnktU2hvb3QucG5nIi8+DQoNCmBgYHtyIGZpZy53aWR0aD05fQ0KU2hhcnBlc3RTaG9vdGVycyAlPiUNCiAgICBnZ3Bsb3QoYWVzKHgzUC4sIEZULiwgc2l6ZSA9IFNob290LiwgY29sb3I9UG9zLCBsYWJlbD1QbGF5ZXIpKSArDQogICAgZ2VvbV9wb2ludChzaGFwZT0xOCkgKw0KICAgIGdlb21fdGV4dChzaXplPTMsIGNvbG9yPSIjMDAwMDAwIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gLTAuMDAyKSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRkZBNTAwIiwgIiNEREREMDAiICwiIzAwMDBGRiIsICIjMzJDRDMyIikpICsNCiAgICBnZ3RpdGxlKCJTaGFycGVzdCBTaG9vdGVycyIpICsNCiAgICB4bGFiKCIzUCUiKSArDQogICAgeWxhYigiRlQlIikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpDQpgYGANCg0KPGJyLz4NCjxici8+DQoNCjwvZGl2Pg0KDQoNCjxici8+DQo8YnIvPg0KDQotLS0NCg0KPGRpdiBjbGFzcz0icm93Ij4NCg0KIDxkaXYgY2xhc3M9ImNvbHVtbiBsZWZ0Ij4NCiA8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczMiIHRhcmdldD0iX2JsYW5rIj4NCjxidXR0b24gY2xhc3M9ImxlZnRidG4iPjwvYnV0dG9uPg0KIDwvYT4NCiA8L2Rpdj4NCiANCiA8ZGl2IGNsYXNzPSJjb2x1bW4gbWlkZGxlIj4NCg0KRW5kIG9mIFNlc3Npb24NCg0KICA8L2Rpdj4NCiAgDQogIDxkaXYgY2xhc3M9ImNvbHVtbiByaWdodCI+DQogIDxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zNSIgdGFyZ2V0PSJfYmxhbmsiPg0KPGJ1dHRvbiBjbGFzcz0icmlnaHRidG4iPjwvYnV0dG9uPg0KICA8L2E+DQogIDwvZGl2Pg0KICANCjwvZGl2Pg0KDQotLS0NCg==