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). We will analyze in-depth each one of them.

Since there’s size limit to upload to rpubs, I must divide this shooting category into two seperated session. Here I will make exploratory analysis on the general shooting stats in the league, Field GOals (FG) and Free Throws (FT). 3-Points and the mixed shooting stats will be for the next session.

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

Start from a wider scope, let’s see the general shooting comparison in the league.

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


Shooting Percentage Density


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac rhoncus ipsum. Nam aliquam bibendum bibendum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla tincidunt metus quis magna posuere euismod. Nam laoreet mollis feugiat. Ut rhoncus libero a ipsum rutrum cursus. Etiam eleifend faucibus interdum.

NBA %>%
    group_by(Pos) %>%
    summarise(FG2m = sum(X2P, na.rm=T),
              FG2a = sum(X2PA, na.rm=T),
              FG2. = round(FG2m/FG2a, 3),
              FG3m = sum(X3P, na.rm=T),
              FG3a = sum(X3PA, na.rm=T),
              FG3. = round(FG3m/FG3a, 3),
              FTm = sum(FT, na.rm=T),
              FTa = sum(FTA, na.rm=T),
              FT. = round(FTm/FTa, 3)) %>%
    select(-c(FG2m:FG2a, FG3m:FG3a, FTm:FTa)) %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, align = "c", caption = "Average Shooting Percentages by Position") %>%
    kable_styling("striped", full_width = F, position = "float_left") %>%
    column_spec(1:4, width = "5em")



Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sit amet mauris in ex ultricies elementum vel rutrum dolor. Phasellus tempor convallis dui, in hendrerit mauris placerat scelerisque. Maecenas a accumsan enim, a maximus velit. Pellentesque in risus eget est faucibus convallis nec at nulla. Phasellus nec lacinia justo. Morbi fermentum, orci id varius accumsan, nibh neque porttitor ipsum, consectetur luctus risus arcu ac ex.

Suspendisse et auctor nisl. Suspendisse cursus ultrices quam non vulputate. Phasellus et pharetra neque, vel feugiat erat. Sed feugiat elit at mauris commodo consequat. Sed congue lectus id mattis hendrerit. Mauris turpis nisl, congue eget velit sed, imperdiet convallis magna.

Non sodales neque sodales ut. Gravida cum sociis natoque penatibus. Nibh cras pulvinar mattis nunc sed blandit libero volutpat sed.

NBA %>%
    group_by(Pos, Player) %>%
    summarise(FG2m = sum(X2P, na.rm=T),
              FG2a = sum(X2PA, na.rm=T),
              FG2. = round(FG2m/FG2a, 3),
              FG3m = sum(X3P, na.rm=T),
              FG3a = sum(X3PA, na.rm=T),
              FG3. = round(FG3m/FG3a, 3),
              FTm = sum(FT, na.rm=T),
              FTa = sum(FTA, na.rm=T),
              FT. = round(FTm/FTa, 3)) %>%
    select(-c(FG2m:FG2a, FG3m:FG3a, FTm:FTa)) %>%
    gather(Parameter, Pct, FG2.:FT., -c(Pos, Player)) %>%
    ggplot(aes(Pct, Pos, fill=Parameter)) +
    ggtitle("Shooting Percentage Density by Position") +
    geom_density_ridges(alpha=0.25) +
    scale_y_discrete(expand = c(0.01, 0), limits = rev(levels(NBA$Pos))) +
    scale_x_continuous(expand = c(0, 0)) +
    scale_fill_manual("Parameter", values = c("blue", "green", "red")) +
    xlab("Percentage") +
    ylab("Position") +
    theme(legend.position="bottom")




NBA Shooting by Year


In dictum ex ligula, nec luctus est bibendum ut. Donec tortor neque, tincidunt sit amet feugiat in, egestas nec orci. Aenean ornare eu lectus et ultricies. Vivamus suscipit tincidunt sapien, ut mollis purus sodales ac. Suspendisse potenti. Nunc eget sapien id lacus molestie sollicitudin eu vitae urna. Etiam a porta ante. Nullam laoreet tortor in mauris hendrerit aliquet ac vel erat. Proin ornare magna in elementum feugiat. Praesent non laoreet sem.

NBA %>%
    group_by(Year) %>%
    summarise(FG2m = sum(X2P, na.rm=T),
              FG2a = sum(X2PA, na.rm=T),
              FG2. = round(FG2m/FG2a, 3),
              FG3m = sum(X3P, na.rm=T),
              FG3a = sum(X3PA, na.rm=T),
              FG3. = round(FG3m/FG3a, 3),
              FTm = sum(FT, na.rm=T),
              FTa = sum(FTA, na.rm=T),
              FT. = round(FTm/FTa, 3)) %>%
    select(-c(FG2m:FG2a, FG3m:FG3a, FTm:FTa)) %>%
    gather(Parameter, Value, FG2.:FT., -Year) %>%
    ggplot() +
    geom_line(aes(Year, Value, group=Parameter, color=Parameter), size = 1.2, alpha = 1) +
    ggtitle("Shooting Percentage by Year") +
    scale_x_continuous(breaks = seq(1950, 2020, 10)) +
    scale_color_manual("Parameter",
                       values = c("blue", "green", "red"),
                       labels = c("FG2%", "FG3%", "FT%")) +
    guides(group=FALSE) +
    theme(legend.position="bottom")




Hit/Missed Shoots by Position


Nulla facilisi. Ut ut euismod eros. Nullam at turpis rhoncus felis ultrices ultrices. Mauris sed ex sit amet tortor euismod elementum. Morbi nec risus et lacus accumsan finibus. Praesent egestas lobortis purus non dictum. Vestibulum vulputate turpis ut vehicula finibus. Nulla facilisi. Curabitur dapibus nulla sit amet placerat consectetur. Mauris eget tortor ultrices, mattis nunc non, porta urna. Duis id augue ornare, convallis nulla efficitur, congue ipsum.

NBA %>%
    group_by(Pos) %>%
    summarise(FG2m = sum(X2P, na.rm = T),
              FG2a = sum(X2PA, na.rm = T) - FG2m,
              FG3m = sum(X3P, na.rm = T),
              FG3a = sum(X3PA, na.rm = T) - FG3m,
              FTm = sum(FT, na.rm = T),
              FTa = sum(FTA, na.rm = T) - FTm) %>%
    gather(Parameter, Count, FG2m:FTa, -Pos) %>%
    mutate(ShootType = as.factor(ifelse(Parameter %in% c("FG2m", "FG2a"), "FG2",
                               ifelse(Parameter %in% c("FG3m", "FG3a"), "FG3",
                                      "FT")))) %>%
    group_by(Pos, ShootType) %>%
    ggplot(aes(ShootType, Count, fill = Parameter)) + 
    geom_bar(stat = 'identity', position = 'stack') +
    facet_grid(~ Pos) +
    ggtitle("Hit/Missed Shoots by Position") +
    guides(fill=FALSE) +
    scale_fill_manual(values=c("#7F7FFF", "#0000FF", "#66B266", "#008000", "#FF4C4C","#FF0000"))




Shooting Hit-Miss by Year


Maecenas et velit tristique, ullamcorper massa in, semper nibh. Vivamus sodales, sem vel venenatis scelerisque, neque metus efficitur velit, vitae gravida magna metus sit amet magna. Duis semper rhoncus dolor, in tincidunt nunc accumsan rutrum. Phasellus eu ligula varius nulla ultricies porta. Proin laoreet pellentesque sem quis placerat. In rutrum magna ut ante iaculis, sit amet tempor velit mollis. Nulla consequat imperdiet mollis. Mauris vestibulum quam quis eros rutrum pharetra. In hac habitasse platea dictumst. Phasellus convallis erat sed blandit blandit. Sed pretium vitae purus sed tempus. Donec ullamcorper nec enim at viverra. Nullam vulputate sit amet urna vitae scelerisque. Nunc id congue elit. Praesent nec velit ut dolor luctus venenatis ut eget dui. Mauris pellentesque pellentesque consectetur.

HitMissFG2 <- NBA %>% group_by(Year) %>%
    summarise(ShootType = "FG2",
              Hit = sum(X2P, na.rm = T) / sum(G),
              Miss = sum(X2PA, na.rm = T) / sum(G))
HitMissFG3 <- NBA %>% group_by(Year) %>%
    summarise(ShootType = "FG3",
              Hit = sum(X3P, na.rm = T) / sum(G),
              Miss = sum(X3PA, na.rm = T) / sum(G))
HitMissFT <- NBA %>% group_by(Year) %>%
    summarise(ShootType = "FT",
              Hit = sum(FT, na.rm = T) / sum(G),
              Miss = sum(FTA, na.rm = T) / sum(G))
HitMissShot <- rbind(HitMissFG2, HitMissFG3, HitMissFT)
HitMissShot %>%
    ggplot(aes(x=Year,
               y=Hit,
               ymin=Hit,
               ymax=Miss,
               fill=ShootType,
               color=ShootType)) + 
    geom_line(size=2) + 
    geom_ribbon(alpha=0.3) +
    ylab("Hit / Missed Shots") +
    scale_linetype_manual(name = "", values = c(1, 1, 1)) +
    scale_color_manual(values=c("#0000FF", "#008000", "#FF0000")) +
    theme(legend.position="bottom")

The thick line shows shooting made and the thin line shows the attempts.







Field Goals(FG)


Field Goals is anything other than Free Throws that convert to points. That means 3-points FG also included in the Field Goals stats. However, this doesn’t necessarily measure ‘shooting’ skill per se, since it also included other method of scoring such slam-dunk, lay-up, even accidental scoring.

Field Goals % Annual League Leaders


Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac felis donec et odio pellentesque diam volutpat. Dictum varius duis at consectetur lorem. Venenatis lectus magna fringilla urna porttitor rhoncus dolor purus non.

TopFG. <- NBA %>%
    filter(FG. > 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),
              FGm = sum(FG),
              FGa = sum(FGA),
              FG. = round(FGm/FGa, 3)) %>%
    filter(ifelse(Year %in% c(1950:1951, 1961-1962), FGm >= 200,
                             ifelse(Year %in% c(1952:1955, 1963-1964, 1966), FGm >= 210,
                               ifelse(Year %in% 1956:1959, FGm >= 230,
                                 ifelse(Year %in% 1960:1961, FGm >= 190,
                                   ifelse(Year %in% c(1965, 1967:1968), FGm >= 220,
                                     ifelse(Year %in% 1969, FGm >= 230,
                                       ifelse(Year %in% 1970, Games >= 70 & FGa >= 700,
                                         ifelse(Year %in% 1971:1972, FGa >= 700,
                                           ifelse(Year %in% 1973:1974, FGa >= 560,
                                             ifelse(Year %in% 1999, FGm >= 183,
                                               ifelse(Year %in% 2012, FGm >= 241,
                                                 FGm >= 300)))))))))))) %>%
    group_by(Year) %>%
    summarise(Player = Player[which.max(FG.)],
              Team = Team[which.max(FG.)],
              Pos = Pos[which.max(FG.)],
              Games = Games[which.max(FG.)],
              MPG = MPG[which.max(FG.)],
              PPG = PPG[which.max(FG.)],
              FGm = FGm[which.max(FG.)],
              FGa = FGa[which.max(FG.)],
              FG. = max(FG., na.rm = T)) %>%
    arrange(desc(Year))
TopFG.  %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "Annual League Leaders Field Goals %") %>%
    kable_styling("striped", "hover", 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 League Leaders Field Goals %
Year Player Team Pos Games MPG PPG FGm FGa FG.
2017 DeAndre Jordan LAC C 81 31.7 12.7 412 577 0.714
2016 DeAndre Jordan LAC C 77 33.7 12.7 357 508 0.703
2015 DeAndre Jordan LAC C 82 34.4 11.5 379 534 0.710
2014 DeAndre Jordan LAC C 82 35.0 10.4 348 515 0.676
2013 DeAndre Jordan LAC C 82 24.5 8.8 314 488 0.643
2012 Tyson Chandler NYK C 62 33.2 11.3 241 355 0.679
2011 Nene Hilario DEN C 75 30.5 14.5 402 654 0.615
2010 Dwight Howard ORL C 82 34.7 18.3 510 834 0.612
2009 Shaquille O'Neal PHO C 75 30.0 17.8 512 841 0.609
2008 Andris Biedrins GSW C 76 27.3 10.5 340 543 0.626
2007 Mikki Moore NJN C 79 26.4 9.8 308 506 0.609
2006 Shaquille O'Neal MIA C 59 30.6 20.0 480 800 0.600
2005 Shaquille O'Neal MIA C 73 34.1 22.9 658 1095 0.601
2004 Shaquille O'Neal LAL C 67 36.8 21.5 554 948 0.584
2003 Eddy Curry CHI C 81 19.4 10.5 335 573 0.585
2002 Shaquille O'Neal LAL C 67 36.1 27.2 712 1229 0.579
2001 Shaquille O'Neal LAL C 74 39.5 28.7 813 1422 0.572
2000 Shaquille O'Neal LAL C 79 40.0 29.7 956 1665 0.574
1999 Shaquille O'Neal LAL C 49 34.8 26.3 510 885 0.576
1998 Shaquille O'Neal LAL C 60 36.2 28.3 670 1147 0.584
1997 Gheorghe Muresan WSB C 73 25.3 10.6 327 541 0.604
1996 Gheorghe Muresan WSB C 76 29.5 14.5 466 798 0.584
1995 Chris Gatling GSW PF 58 25.3 13.7 324 512 0.633
1994 Shaquille O'Neal ORL C 81 39.8 29.3 953 1591 0.599
1993 Cedric Ceballos PHO SF 74 21.7 12.8 381 662 0.576
1992 Buck Williams POR PF 80 31.5 11.3 340 563 0.604
1991 Buck Williams POR PF 80 32.3 11.7 358 595 0.602
1990 Mark West PHO C 82 29.3 10.5 331 530 0.625
1989 Dennis Rodman DET SF 82 26.9 9.0 316 531 0.595
1988 Kevin McHale BOS PF 64 37.3 22.6 550 911 0.604
1987 Kevin McHale BOS PF 77 39.7 26.1 790 1307 0.604
1986 Steve Johnson SAS C 71 25.7 13.8 362 573 0.632
1985 James Donaldson LAC C 82 29.2 11.3 351 551 0.637
1984 Artis Gilmore SAS C 64 31.8 15.3 351 556 0.631
1983 Artis Gilmore SAS C 82 34.1 18.0 556 888 0.626
1982 Artis Gilmore CHI C 82 34.1 18.5 546 837 0.652
1981 Artis Gilmore CHI C 82 34.5 17.9 547 816 0.670
1980 Cedric Maxwell BOS SF 80 34.3 16.9 457 750 0.609
1979 Cedric Maxwell BOS SF 80 37.1 19.0 472 808 0.584
1978 Bobby Jones DEN PF 75 32.5 14.5 440 761 0.578
1977 Kareem Abdul-Jabbar LAL C 82 36.8 26.2 888 1533 0.579
1976 John Shumate PHO PF 75 26.3 11.7 332 592 0.561
1975 Don Nelson BOS SF 79 26.0 14.0 423 785 0.539
1974 Bob McAdoo BUF C 74 43.0 30.6 901 1647 0.547
1973 Wilt Chamberlain LAL C 82 43.2 13.2 426 586 0.727
1972 Wilt Chamberlain LAL C 82 42.3 14.8 496 764 0.649
1971 Johnny Green CIN PF 75 28.6 16.7 502 855 0.587
1970 Johnny Green CIN PF 78 29.2 15.6 481 860 0.559
1969 Wilt Chamberlain LAL C 81 45.3 20.5 641 1099 0.583
1968 Wilt Chamberlain PHI C 82 46.8 24.3 819 1377 0.595
1967 Wilt Chamberlain PHI C 81 45.5 24.1 785 1150 0.683
1966 Wilt Chamberlain PHI C 79 47.3 33.5 1074 1990 0.540
1965 Wilt Chamberlain SFW C 73 45.2 34.7 1063 2083 0.510
1964 Jerry Lucas CIN PF 79 41.4 17.7 545 1035 0.527
1963 Wilt Chamberlain SFW C 80 47.6 44.8 1463 2770 0.528
1962 Walt Bellamy CHP C 79 42.3 31.6 973 1875 0.519
1961 Wilt Chamberlain PHW C 79 47.8 38.4 1251 2457 0.509
1960 Kenny Sears NYK PF 64 32.8 18.5 412 863 0.477
1959 Kenny Sears NYK PF 71 35.2 21.0 491 1002 0.490
1958 Jack Twyman CIN SF 72 30.2 17.2 465 1028 0.452
1957 Neil Johnston PHW C 69 36.7 22.8 520 1163 0.447
1956 Neil Johnston PHW C 70 37.1 22.1 499 1092 0.457
1955 Larry Foust FTW C 70 32.3 17.0 398 818 0.487
1954 Ed Macauley BOS C 71 39.3 18.9 462 950 0.486
1953 Ed Macauley BOS C 69 42.1 20.3 451 997 0.452
1952 Paul Arizin PHW SF 66 44.5 25.4 548 1222 0.448
1951 Alex Groza INO C 66 NA 21.7 492 1046 0.470
1950 Alex Groza INO C 64 NA 23.4 521 1090 0.478

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


Generating plot…

meanFG.Year <- NBA %>%
    group_by(Year) %>%
    filter(FG. > 0) %>%
    summarise(FG. = sum(FG)/sum(FGA))

TFG. <- TopFG. %>%
    ggplot() +
    geom_bar(aes(Year, FG., fill = Pos, text = paste("Player:", Player)), stat = "identity") +
    geom_line(data=meanFG.Year, aes(Year, FG., linetype = "Average line"), color = "black") +
    ggtitle("Annual Field Goal Percentage Leaders") +
    geom_hline(yintercept = mean(TopFG.$FG.), col = "blue", alpha = 0.5) +
    scale_x_continuous(breaks = seq(1950, 2017, 10)) +
    scale_fill_manual("Pos", values = PosColorCode) +
    theme(legend.position="none")

pTFG <- ggplotly(TFG.)

#api_create(pTFG, filename = "AnnualFG.Leaders")
AnnualFG.Leaders

click image for interactive plotly graph

  • Average top FG% is 0.567.
  • Highest top FG% is 0.727, in season 1973.
  • Lowest top FG% is 0.447, in season 1957.
  • The Centers dominated the league by 72.1%, followed by PF with 17.6%, then SF
    complete the leaderboard with 10.3%. No guard ever be in FG% leaderboard, this because guards territory is outside, while post players (C / PF) positioned near the basket have greater chance of success in delivering the ball to the basket.




All-Time Field Goals % Leaders


Quis eleifend quam adipiscing vitae proin sagittis. Magna fermentum iaculis eu non diam phasellus vestibulum. Pharetra et ultrices neque ornare aenean. Cursus mattis molestie a iaculis at erat. Elementum pulvinar etiam non quam lacus suspendisse faucibus. Placerat orci nulla pellentesque dignissim enim sit amet.

AllTimeFG. <- NBA %>%
    filter(FG. > 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),
              FGm = sum(FG),
              FGa = sum(FGA),
              FG. = round(FGm/FGa, 3)) %>%
    filter(FGm >= 2000) %>%
    arrange(desc(FG.), desc(FGm)) %>%
    mutate(Rank = min_rank(desc(FG.))) %>%
    select(Rank, everything())
AllTimeFG. %>%
    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 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 Field Goals % Leaders
Rank Player Pos Team ActiveYears Games MPG PPG FGm FGa FG.
1 DeAndre Jordan C LAC 2009 - 2018 673 27.7 9.1 2487 3674 0.677
2 Artis Gilmore C CHI 1972 - 1988 909 32.7 17.1 5732 9570 0.599
3 Tyson Chandler C CHI 2002 - 2018 1033 28.4 8.7 3362 5660 0.594
4 Dwight Howard C ORL 2005 - 2018 954 34.9 17.5 5988 10237 0.585
5 Shaquille O'Neal C LAL 1993 - 2011 1207 34.7 23.7 11330 19457 0.582
6 Mark West C PHO 1984 - 2000 1089 18.6 5.7 2528 4355 0.580
7 Amir Johnson PF TOR 2006 - 2018 745 22.3 7.5 2339 4073 0.574
8 Darryl Dawkins C PHI 1976 - 1989 726 23.7 12.0 3477 6079 0.572
8 Steve Johnson C KCK 1982 - 1991 622 21.9 11.8 2841 4963 0.572
10 James Donaldson C DAL 1981 - 1995 957 27.4 8.6 3105 5442 0.571

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


Generating plot…

LabelFix <- AllTimeFG. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(FG.), fun=mean),
           FGa = FGa-FGm,
           FG. = paste(FG. * 100, "%")) %>%
    gather(Parameter, Count, FGm:FGa, -c(Player, FG.)) %>%
    filter(Parameter == "FGa")
    
AllTimeFG. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(FG.), fun=mean),
           FGa = FGa-FGm) %>%
    gather(Parameter, Count, FGm:FGa, -c(Player, FG.)) %>%
    ggplot(aes(Player, Count, fill = Parameter)) + 
    geom_bar(stat = 'identity', position = 'stack') +
    geom_label(aes(label = FG.,
                   y = 21000),
               data = LabelFix,
               stat = 'identity',
               position = "stack",
               fill = "#FF4000",
               color = "#00FFFF",
               size = 4.5) +
    ggtitle("All-Time Field Goals Percentage Leaders") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    scale_fill_manual(labels=c("FG-missed  ", "FG-made"), values=c("#7F7FFF", "#0000FF")) +
    theme(legend.title=element_blank()) +
    theme(legend.position="bottom")


Since the leaderboard is totally occupied by post players, naturally I’d like to see the best FG% in each position, so here it is.

TopFG.Pos <- AllTimeFG. %>%
    group_by(Pos) %>%
    summarise(Player = Player[which.max(FG.)],
              Team = Team[which.max(FG.)],
              ActiveYears = ActiveYears[which.max(FG.)],
              Games = Games[which.max(FG.)],
              MPG = MPG[which.max(FG.)],
              PPG = PPG[which.max(FG.)],
              FGm = FGm[which.max(FG.)],
              FGa = FGa[which.max(FG.)],
              FG. = FG.[which.max(FG.)])

Best FG% by Position

C


DeAndre Jordan

0.677
  • FG: 2487 / 3674
  • PPG: 9.1
  • Years: 2009 - 2018

PF


Amir Johnson

0.574
  • FG: 2339 / 4073
  • PPG: 7.5
  • Years: 2006 - 2018

SF


Cedric Maxwell

0.546
  • FG: 3433 / 6293
  • PPG: 12.5
  • Years: 1978 - 1988

SG


Lewis Lloyd

0.524
  • FG: 2172 / 4144
  • PPG: 13.2
  • Years: 1982 - 1990

PG


Maurice Cheeks

0.523
  • FG: 4906 / 9374
  • PPG: 11.1
  • Years: 1979 - 1993





Field Goals per 36 Minutes


Lorem ipsum dolor sit amet, malis possim deleniti qui at, eos alia decore scripserit et, ex est dicant docendi. Ne has invenire voluptatibus. Pri id vivendo vulputate theophrastus! Te vim definitionem vituperatoribus, graece singulis interpretaris mei ei? Pri eu menandri senserit?


AllTimeFGma <- 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 / Games, 1),
              PPG = round(sum(PTS)/sum(G), 1),
              FGm_36 = round((sum(FG) / Minutes) * 36, 1),
              FGm_G = round((sum(FG) / Games), 1),
              FGa_36 = round((sum(FGA) / Minutes) *36, 1),
              FGa_G = round((sum(FGA) / Games), 1),
              FG. = round(sum(FG)/sum(FGA), 3)) %>%
    select(-"Minutes")
AllTimeFGma %>%
    arrange(desc(FGm_36), desc(FG.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(FGm_36))) %>%
    select(Rank, everything()) %>%
    kable(escape = FALSE, caption = "Field Goals 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")
Field Goals Made per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG FGm_36 FGm_G FGa_36 FGa_G FG.
1 George Gervin SG SAS 1973 - 1986 791 33.5 26.2 10.9 10.2 21.4 19.9 0.511
2 Michael Jordan SG CHI 1985 - 2003 1072 38.3 30.1 10.7 11.4 21.5 22.9 0.497
3 Walter Davis SG PHO 1978 - 1992 1033 27.9 18.9 10.1 7.9 19.8 15.4 0.511
3 Alex English SF DEN 1977 - 1991 1193 31.9 21.5 10.1 8.9 19.9 17.6 0.507
5 Kareem Abdul-Jabbar C LAL 1970 - 1989 1560 36.8 24.6 9.9 10.2 17.7 18.1 0.559
6 Shaquille O'Neal C LAL 1993 - 2011 1207 34.7 23.7 9.7 9.4 16.7 16.1 0.582
7 Bernard King SF NYK 1978 - 1993 874 33.7 22.5 9.6 9.0 18.5 17.3 0.518
8 Wilt Chamberlain C LAL 1960 - 1973 1045 45.8 30.1 9.5 12.1 17.7 22.5 0.540
9 Bob McAdoo C BUF 1973 - 1986 852 33.2 22.1 9.4 8.7 18.7 17.3 0.503
9 Dominique Wilkins SF ATL 1983 - 1999 1074 35.5 24.8 9.4 9.3 20.4 20.1 0.461
AllTimeFGma %>%
    arrange(desc(FGa_36), desc(FG.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(FGa_36))) %>%
    select(Rank, everything()) %>%
    kable(escape = FALSE, caption = "Field Goal 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")
Field Goal Attempts per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG FGm_36 FGm_G FGa_36 FGa_G FG.
1 Tom Heinsohn PF BOS 1957 - 1965 654 29.4 18.6 8.9 7.3 22.0 18.0 0.405
2 Michael Jordan SG CHI 1985 - 2003 1072 38.3 30.1 10.7 11.4 21.5 22.9 0.497
3 George Gervin SG SAS 1973 - 1986 791 33.5 26.2 10.9 10.2 21.4 19.9 0.511
3 Elgin Baylor SF LAL 1959 - 1972 846 40.0 27.4 9.2 10.3 21.4 23.8 0.431
5 Pete Maravich SG NOJ 1971 - 1980 658 37.0 24.2 9.2 9.4 20.8 21.3 0.441
6 Dominique Wilkins SF ATL 1983 - 1999 1074 35.5 24.8 9.4 9.3 20.4 20.1 0.461
6 Sam Jones SG BOS 1958 - 1969 871 27.9 17.7 9.3 7.2 20.4 15.8 0.456
6 Clyde Lovellette C MNL 1954 - 1964 704 27.1 17.0 9.0 6.8 20.4 15.3 0.443
9 Rick Barry SF GSW 1966 - 1980 794 36.3 23.2 9.1 9.1 20.2 20.4 0.449
10 Alex English SF DEN 1977 - 1991 1193 31.9 21.5 10.1 8.9 19.9 17.6 0.507


Probo efficiendi sea ea, ne duis argumentum persequeris vel. Ut duo ferri ipsum, duo vidit error no, ad sed dicta suavitate dissentiet. Ne audiam vocibus est. Posse invenire vix in.

AllTimeFGma %>%
    arrange(desc(FGm_36), desc(FG.)) %>%
    mutate(Player = reorder(Player, desc(FGm_36), FUN=mean)) %>%
    head(n=10) %>%
    ggplot(aes(group = 1)) +
    geom_segment(aes(x=Player, xend=Player, y=FGm_36, yend=FGa_36), color="black") +
    geom_point(aes(Player, FGm_36, color="#FF5800"), size=5) +
    geom_point(aes(Player, FGa_36, color="#FF5800"), size=2.5) +
    geom_point(aes(Player, FGm_G, color="#009dff"), size=5, shape=18) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    ggtitle("FG Made per 36 Minutes vs. FG Made per Game") +
    xlab("") +
    ylab("FG Made") +
    scale_color_manual(name="", values=c("#009dff", "#FF5800"),
                       labels=c("FGa/m per Game", "FGa/m per 36 minutes"),
                       guide = guide_legend(reverse=TRUE)) +
    theme(legend.position='none')




BIG names in Field Goals


For shooting, in general, I’d like to apply simpler approach, emphasizing percentage over made by doubling it. Field Goals will be sorted according to the following arbritary criteria :

  • Annual league leader in Field Goals percentage category earn 2 point.
  • Top 10 all-time leaders in Field Goals percentage category earn 2 points.
  • Top 10 all-time leaders in Field Goals made category with FG% more than 55% earn 1 point.
  • Top 10 all-time leaders in Field Goals made per 36 minutes category with FG% more than 55% earn 1 point.

Calculate the points and we have the rank!

# Filter total Field Goals made with FG% > .55
AllTimeFGm <- NBA %>%
    group_by(Player) %>%
    summarise(FGm = sum(FG),
              FGa = sum(FGA),
              FG. = FGm / FGa) %>%
    filter(FG. > 0.55) %>%
    arrange(desc(FGm)) %>% head(10)
# Filter Field Goals made per 36 minutes with FG% > .55
AllTimeFGm36 <- AllTimeFGma %>%
    filter(FG. >= 0.55) %>%
    arrange(desc(FGm_36)) %>%
    head(n=10)
NamesFG <- data.frame(Name = c(as.character(rep(TopFG.$Player, 2)),
                               as.character(rep(head(AllTimeFG.$Player, 10), 2)),
                               as.character(AllTimeFGm$Player),
                               as.character(AllTimeFGm36$Player)))
NamesFG %>% count(Name) %>% arrange(desc(n)) %>% 
    wordcloud2(size=0.4, color='random-light', backgroundColor="black")







Free Throw (FT)

In the two categories above (FG and 3P), a player can deliberately choose to make a shot attempt. However, in FT they need to be fouled by opponent player to attempt the shot.

Quite often, a player with good FG% but bad FT%, which is common for a big guy (Centers or Power Forward), will get fouled intentionally by the opponent. Shaquille O’Neal (C), with his 58.2% FG and 52.7% FT, is the most notable example for this, hence the term Hack-a-Shaq.

Now let’s get started..


Annual Free Throws % League Leaders


Mauris iaculis ligula eleifend dolor semper, id dignissim dui dignissim. Pellentesque neque ligula, sagittis quis enim vitae, pretium scelerisque magna. Quisque in posuere elit, quis luctus mauris. Proin eget auctor lectus. Cras tincidunt laoreet elit, ac ultricies justo pulvinar eget. Aliquam pretium facilisis augue in scelerisque. Nunc luctus venenatis sollicitudin. Vivamus facilisis non magna nec imperdiet. Curabitur eu libero at magna tincidunt auctor. Quisque laoreet dapibus sollicitudin.

TopFT. <- NBA %>%
    filter(FT. > 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),
              FTm = sum(FT),
              FTa = sum(FTA),
              FT. = round(FTm/FTa, 3)) %>%
    filter(ifelse(Year %in% 1950:1951, FTm >= 170,
             ifelse(Year %in% 1952:1955, FTm >= 180,
               ifelse(Year %in% 1956:1959, FTm >= 190,
                 ifelse(Year %in% 1960, FTm >= 185,
                   ifelse(Year %in% 1961:1962, FTm >= 200,
                     ifelse(Year %in% 1963:1966, FTm >= 210,
                       ifelse(Year %in% 1967:1968, FTm >= 220,
                         ifelse(Year %in% 1969, FTm >= 230,
                           ifelse(Year %in% 1970, Games >= 70 & FTa >= 350,
                             ifelse(Year %in% 1971:1972, FTa >= 350,
                               ifelse(Year %in% 1973:1974, FTa >= 160,
                                 ifelse(Year %in% 1999, FTm >= 76,
                                   ifelse(Year %in% 2012, FTm >= 100,
                                     FTm >= 125)))))))))))))) %>%
    group_by(Year) %>%
    summarise(Player = Player[which.max(FT.)],
              Team = Team[which.max(FT.)],
              Pos = Pos[which.max(FT.)],
              Games = Games[which.max(FT.)],
              MPG = MPG[which.max(FT.)],
              PPG = PPG[which.max(FT.)],
              FTm = FTm[which.max(FT.)],
              FTa = FTa[which.max(FT.)],
              FT. = max(FT., na.rm = T)) %>%
    arrange(desc(Year))
TopFT. %>%
    mutate(Pos = cell_spec(Pos, color = "white", align = "c", 
                    background = factor(Pos, c("C", "PF", "SF", "SG", "PG"), 
                                        PosColorCode))) %>%
    kable(escape = FALSE, caption = "Annual Free Throws % 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 Free Throws % League Leaders
Year Player Team Pos Games MPG PPG FTm FTa FT.
2017 C.J. McCollum POR SG 80 35.0 23.0 268 294 0.912
2016 Stephen Curry GSW PG 79 34.2 30.1 363 400 0.908
2015 Stephen Curry GSW PG 80 32.7 23.8 308 337 0.914
2014 Brian Roberts NOP PG 72 23.2 9.4 125 133 0.940
2013 Kevin Durant OKC SF 81 38.5 28.1 679 750 0.905
2012 Jamal Crawford POR PG 60 26.9 13.9 191 206 0.927
2011 Stephen Curry GSW PG 74 33.6 18.6 212 227 0.934
2010 Steve Nash PHO PG 81 32.8 16.5 211 225 0.938
2009 Jose Calderon TOR PG 68 34.3 12.8 151 154 0.981
2008 Peja Stojakovic NOH SF 77 35.2 16.4 130 140 0.929
2007 Kyle Korver PHI SF 74 30.9 14.4 191 209 0.914
2006 Steve Nash PHO PG 79 35.4 18.8 257 279 0.921
2005 Reggie Miller IND SG 66 31.9 14.8 250 268 0.933
2004 Peja Stojakovic SAC SF 81 40.3 24.2 394 425 0.927
2003 Allan Houston NYK SG 82 37.9 22.5 363 395 0.919
2002 Reggie Miller IND SG 79 36.6 16.5 296 325 0.911
2001 Reggie Miller IND SG 81 39.3 18.9 323 348 0.928
2000 Jeff Hornacek UTA SG 77 27.7 12.4 171 180 0.950
1999 Reggie Miller IND SG 50 35.7 18.4 226 247 0.915
1998 Chris Mullin IND SF 82 26.5 11.3 154 164 0.939
1997 Mark Price GSW PG 70 26.8 11.3 155 171 0.906
1996 Mahmoud Abdul-Rauf DEN PG 57 35.6 19.2 146 157 0.930
1995 Spud Webb SAC PG 76 32.3 11.6 226 242 0.934
1994 Mahmoud Abdul-Rauf DEN PG 80 32.7 18.0 219 229 0.956
1993 Mark Price CLE PG 75 31.7 18.2 289 305 0.948
1992 Mark Price CLE PG 72 29.7 17.3 270 285 0.947
1991 Reggie Miller IND SG 82 36.2 22.6 551 600 0.918
1990 Larry Bird BOS SF 75 39.3 24.3 319 343 0.930
1989 Magic Johnson LAL PG 77 37.5 22.5 513 563 0.911
1988 Jack Sikma MIL C 82 35.6 16.5 321 348 0.922
1987 Larry Bird BOS SF 74 40.6 28.1 414 455 0.910
1986 Chris Mullin GSW SG 55 25.3 14.0 189 211 0.896
1985 Kyle Macy PHO PG 65 31.0 11.0 127 140 0.907
1984 Larry Bird BOS PF 79 38.3 24.2 374 421 0.888
1983 Calvin Murphy HOU PG 64 22.2 12.8 138 150 0.920
1982 Kyle Macy PHO PG 82 34.7 14.2 152 169 0.899
1981 Calvin Murphy HOU PG 76 26.5 16.7 206 215 0.958
1980 Rick Barry HOU SF 72 25.2 12.0 143 153 0.935
1979 Rick Barry HOU SF 80 32.1 13.5 160 169 0.947
1978 Rick Barry GSW SF 82 36.9 23.1 378 409 0.924
1977 Ernie DiGregorio BUF PG 81 28.0 10.7 138 146 0.945
1976 Rick Barry GSW SF 81 38.5 21.0 287 311 0.923
1975 Rick Barry GSW SF 80 40.4 30.6 394 436 0.904
1974 Ernie DiGregorio BUF PG 81 35.9 15.2 174 193 0.902
1973 Rick Barry GSW SF 82 37.5 22.3 358 397 0.902
1972 Jack Marin BAL SF 78 37.5 22.3 356 398 0.894
1971 Chet Walker CHI SF 81 36.1 22.0 480 559 0.859
1970 Flynn Robinson MIL PG 81 34.1 21.8 439 489 0.898
1969 Larry Siegfried BOS PG 79 32.4 14.2 336 389 0.864
1968 Oscar Robertson CIN PG 65 42.5 29.2 576 660 0.873
1967 Adrian Smith CIN SG 81 32.5 16.6 343 380 0.903
1966 Larry Siegfried BOS PG 71 23.6 13.7 274 311 0.881
1965 Larry Costello PHI PG 64 30.7 13.5 243 277 0.877
1964 Oscar Robertson CIN PG 79 45.1 31.4 800 938 0.853
1963 Larry Costello SYR PG 78 26.5 11.0 288 327 0.881
1962 Dolph Schayes SYR PF 56 26.4 14.7 286 319 0.897
1961 Bill Sharman BOS SG 61 25.2 16.0 210 228 0.921
1960 Dolph Schayes SYR PF 75 36.5 22.5 533 597 0.893
1959 Bill Sharman BOS SG 72 33.1 20.4 342 367 0.932
1958 Dolph Schayes SYR PF 72 40.5 24.9 629 696 0.904
1957 Bill Sharman BOS SG 67 35.9 21.1 381 421 0.905
1956 Bill Sharman BOS SG 72 37.5 19.9 358 413 0.867
1955 Bill Sharman BOS SG 68 36.1 18.4 347 387 0.897
1954 Bill Sharman BOS SG 72 34.3 16.0 331 392 0.844
1953 Bill Sharman BOS SG 71 32.9 16.2 341 401 0.850
1952 Bobby Wanzer ROC SG 66 37.8 15.7 377 417 0.904
1951 Joe Fulks PHW PF 66 NA 18.7 378 442 0.855
1950 Max Zaslofsky CHS SG 68 NA 16.4 321 381 0.843

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


Generating plot…

meanFT.Year <- NBA %>%
    group_by(Year) %>%
    filter(FT. > 0) %>%
    summarise(FT. = sum(FT)/sum(FTA))

TFT. <- TopFT. %>%
    ggplot() +
    geom_bar(aes(Year, FT., fill = Pos, text = paste("Player:", Player)), stat = "identity") +
    geom_line(data=meanFT.Year, aes(Year, FT., linetype = "Average line"), color = "black") +
    ggtitle("Annual Free Throws Percentage Leaders") +
    geom_hline(yintercept = mean(TopFT.$FT.), col = "blue", alpha = 0.5) +
    scale_x_continuous(breaks = seq(1950, 2020, 10)) +
    scale_fill_manual("Pos", values = PosColorCode) +
    theme(legend.position="none")

pTFT. <- ggplotly(TFT.)

#api_create(pTFT., filename = "AnnualFT% Leaders")
AnnualFT% Leaders


  • Average top FT% is 0.903.
  • Highest top FT% is 0.981, in season 2009.
  • Lowest top FT% is 0.843, in season 1950.
  • The PG dominated the league by 41.2%, SG with 27.9%, followed by , then SF
    added with 22.1%, and finally PF completed the leaderboard with only 7.4%. And there’s only 1 center ever in the annual FT% leaderboard, Jack Sikma with 0.922 FT.




All-Time Free Throws % Leaders


Aenean id consequat eros. Vivamus vitae cursus nulla, non vulputate lacus. Vestibulum tempor ornare dui a fermentum. Fusce aliquet leo in justo eleifend rhoncus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin mollis leo a odio aliquet consequat. Donec molestie fringilla convallis. Ut ut ullamcorper quam, non sagittis turpis. Praesent fermentum maximus tortor, id molestie sem fringilla egestas. Etiam in dapibus quam, id porttitor lorem.

AllTimeFT. <- NBA %>%
    filter(FT. > 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),
              FTm = sum(FT),
              FTa = sum(FTA),
              FT. = round(FTm/FTa, 3)) %>%
    filter(FTm >= 1200) %>%
    arrange(desc(FT.), desc(FTm)) %>%
    mutate(Rank = min_rank(desc(FT.))) %>%
    select(Rank, everything())
AllTimeFT. %>%
    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 Free Throws % 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 Free Throws % Leaders
Rank Player Pos Team ActiveYears Games MPG PPG FTm FTa FT.
1 Steve Nash PG PHO 1997 - 2014 1217 31.3 14.3 3060 3384 0.904
1 Mark Price PG CLE 1987 - 1998 722 29.9 15.2 2135 2362 0.904
3 Stephen Curry PG GSW 2010 - 2018 574 34.7 22.8 1993 2212 0.901
4 Rick Barry SF GSW 1966 - 1980 794 36.3 23.2 3818 4243 0.900
5 Peja Stojakovic SF SAC 1999 - 2011 804 33.5 17.0 2237 2500 0.895
6 Chauncey Billups PG DET 1998 - 2014 1043 31.6 15.2 4496 5029 0.894
6 Ray Allen SG MIL 1997 - 2014 1300 35.6 18.9 4398 4920 0.894
8 Calvin Murphy PG HOU 1971 - 1983 1002 30.5 17.9 3445 3864 0.892
9 Scott Skiles PG ORL 1987 - 1996 600 28.0 11.1 1548 1741 0.889
10 Reggie Miller SG IND 1988 - 2005 1389 34.3 18.2 6237 7026 0.888


The rank table shows there are two players sharing the first spot. But actually, if the number not rounded, Steve Nash has 0.9042553 FT, while Mark Price has 0.903895 FT. So Steve Nash won the top spot!

LabelFix <- AllTimeFT. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(FT.), fun=mean),
           FTa = FTa-FTm,
           FT. = paste(FT. * 100, "%")) %>%
    gather(Parameter, Count, FTm:FTa, -c(Player, FT.)) %>%
    filter(Parameter == "FTa")
AllTimeFT. %>%
    head(10) %>%
    mutate(Player = fct_reorder(Player, desc(FT.), fun=mean),
           FTa = FTa-FTm) %>%
    gather(Parameter, Count, FTm:FTa, -c(Player, FT.)) %>%
    ggplot(aes(Player, Count, fill = Parameter)) + 
    geom_bar(stat = 'identity', position = 'stack') +
    geom_label(aes(label = FT.,
                   y = 7500),
               data = LabelFix,
               stat = 'identity',
               position = "stack",
               fill = "#FF4000",
               color = "#00FFFF",
               size = 4.5) +
    ggtitle("All-Time Free Throws Percentage Leaders") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    scale_fill_manual(labels=c("FT-missed  ", "FT-made"), values=c("#FF4C4C","#FF0000")) +
    theme(legend.title=element_blank()) +
    theme(legend.position="bottom")


And here they are…

TopFT.Pos <- AllTimeFT. %>%
    group_by(Pos) %>%
    summarise(Player = Player[which.max(FT.)],
              Team = Team[which.max(FT.)],
              ActiveYears = ActiveYears[which.max(FT.)],
              Games = Games[which.max(FT.)],
              MPG = MPG[which.max(FT.)],
              PPG = PPG[which.max(FT.)],
              FTm = FTm[which.max(FT.)],
              FTa = FTa[which.max(FT.)],
              FT. = FT.[which.max(FT.)])

Best FT% by Position

C


Jack Sikma

0.849
  • FG: 4292 / 5053
  • PPG: 15.6
  • Years: 1978 - 1991

PF


Dirk Nowitzki

0.879
  • FG: 7104 / 8081
  • PPG: 21.7
  • Years: 1999 - 2018

SF


Rick Barry

0.9
  • FG: 3818 / 4243
  • PPG: 23.2
  • Years: 1966 - 1980

SG


Ray Allen

0.894
  • FG: 4398 / 4920
  • PPG: 18.9
  • Years: 1997 - 2014

PG


Steve Nash

0.904
  • FG: 3060 / 3384
  • PPG: 14.3
  • Years: 0.904




Free Throws per 36 Minutes Leaders


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus fermentum turpis et justo fermentum viverra. Aliquam ipsum mi, dignissim quis eros fermentum, posuere tincidunt nulla. Quisque sollicitudin, massa quis lacinia accumsan, quam urna finibus augue, in blandit purus nisi id nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras efficitur nisl a interdum accumsan. Suspendisse et maximus metus, vel accumsan arcu. Vivamus mollis quis eros ut molestie.

AllTimeFTma <- NBA %>%
    group_by(Player) %>%
    filter(sum(G) >= 400) %>%
    summarise(Pos = getmode(Pos),
              Team = getmode(Tm),
              ActiveYears = paste(getmode(YearStart), "-", getmode(YearEnd)),
              Minutes = sum(MP),
              Games = sum(G),
              MPG = round(Minutes /sum(G), 1),
              PPG = round(sum(PTS)/sum(G), 1),
              FTm_36 = round((sum(FT) / Minutes) * 36, 1),
              FTm_G = round(sum(FT) / Games, 1),
              FTa_36 = round((sum(FTA) / Minutes) * 36, 1),
              FTa_G = round(sum(FTA) / Games, 1),
              FT. = round(sum(FT)/sum(FTA), 3)) %>%
    select(-"Minutes")
AllTimeFTma %>%
    arrange(desc(FTm_36), desc(FT.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(FTm_36))) %>%
    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 = "Free Throw 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") %>%
    column_spec(13, bold = T) %>%
    scroll_box(width = "100%", height = "300px")
Free Throw Made per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG FTm_36 FTm_G FTa_36 FTa_G FT.
1 James Harden SG HOU 2010 - 2018 615 33.6 22.1 7.4 6.9 8.6 8.1 0.854
2 Bob Pettit PF STL 1955 - 1965 792 38.8 26.4 7.3 7.8 9.5 10.3 0.761
3 Adrian Dantley SF UTA 1977 - 1991 955 35.8 24.3 7.2 7.2 8.8 8.7 0.818
4 Corey Maggette SF LAC 2000 - 2013 827 28.2 16.0 7.1 5.6 8.6 6.8 0.822
4 John Drew SF ATL 1975 - 1985 739 29.5 20.7 7.1 5.8 9.5 7.8 0.748
6 Jerry West PG LAL 1961 - 1974 932 39.2 27.0 7.0 7.7 8.7 9.4 0.814
7 Kevin Durant SF OKC 2008 - 2018 703 37.4 27.2 6.8 7.0 7.7 8.0 0.882
7 Moses Malone C HOU 1975 - 1995 1329 33.9 20.6 6.8 6.4 8.9 8.3 0.769
9 Neil Johnston C PHW 1952 - 1959 516 35.5 19.4 6.7 6.6 8.7 8.6 0.768
10 Michael Jordan SG CHI 1985 - 2003 1072 38.3 30.1 6.4 6.8 7.7 8.2 0.835


AllTimeFTma %>%
    arrange(desc(FTa_36), desc(FT.)) %>%
    head(n=10) %>%
    mutate(Rank = min_rank(desc(FTa_36))) %>%
    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 = "Free Throw 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") %>%
    column_spec(13, bold = T) %>%
    scroll_box(width = "100%", height = "300px")
Free Throw Attempts per 36 Minutes
Rank Player Pos Team ActiveYears Games MPG PPG FTm_36 FTm_G FTa_36 FTa_G FT.
1 Shaquille O'Neal C LAL 1993 - 2011 1207 34.7 23.7 5.1 4.9 9.7 9.3 0.527
2 Bob Pettit PF STL 1955 - 1965 792 38.8 26.4 7.3 7.8 9.5 10.3 0.761
2 John Drew SF ATL 1975 - 1985 739 29.5 20.7 7.1 5.8 9.5 7.8 0.748
4 Moses Malone C HOU 1975 - 1995 1329 33.9 20.6 6.8 6.4 8.9 8.3 0.769
4 Dwight Howard C ORL 2005 - 2018 954 34.9 17.5 5.1 4.9 8.9 8.7 0.566
4 Wilt Chamberlain C LAL 1960 - 1973 1045 45.8 30.1 4.6 5.8 8.9 11.4 0.511
7 Adrian Dantley SF UTA 1977 - 1991 955 35.8 24.3 7.2 7.2 8.8 8.7 0.818
8 Jerry West PG LAL 1961 - 1974 932 39.2 27.0 7.0 7.7 8.7 9.4 0.814
8 Neil Johnston C PHW 1952 - 1959 516 35.5 19.4 6.7 6.6 8.7 8.6 0.768
8 Karl Malone PF UTA 1986 - 2004 1476 37.2 25.0 6.4 6.6 8.7 8.9 0.742


Nulla facilisis, sem eget porta luctus, leo diam molestie elit, lobortis ultricies enim nunc eu velit. Curabitur tristique sit amet urna id porttitor. Duis eu pulvinar dolor. Quisque fermentum lobortis interdum. Morbi consequat ipsum ut enim eleifend, aliquet interdum ligula tempor. Cras sed ornare eros. Aliquam vulputate ac purus eget laoreet. Integer tempus ex urna, id posuere risus lacinia eget. Suspendisse id tortor enim. Pellentesque tristique diam efficitur, maximus augue a, tincidunt sem. Fusce luctus nulla auctor, interdum enim quis, eleifend mi. Maecenas in sapien tortor.

AllTimeFTma %>%
    arrange(desc(FTm_36), desc(FT.)) %>%
    mutate(Player = reorder(Player, desc(FTm_36), FUN=mean)) %>%
    head(n=10) %>%
    ggplot(aes(group = 1)) +
    geom_segment(aes(x=Player, xend=Player, y=FTm_36, yend=FTa_36), color="black") +
    geom_point(aes(Player, FTm_36, color="#FF5800"), size=5) +
    geom_point(aes(Player, FTa_36, color="#FF5800"), size=2.5) +
    geom_point(aes(Player, FTm_G, color="#009dff"), size=5, shape=18) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    ggtitle("Free Throw Made per 36 Minutes vs. Free Throw Made per Game") +
    xlab("") +
    ylab("FT Made") +
    scale_color_manual(name="", values=c("#009dff", "#FF5800"),
                       labels=c("FT a/m per Game", "FT a/m per 36 minutes"),
                       guide = guide_legend(reverse=TRUE)) +
    theme(legend.position='none')

Shorter line means better percentage, and vice versa.




BIG Free Thrower Names


The Free Throwers will be sorted according to the following arbritary criteria :

  • Annual league leader in Free Throw percentage category earn 1 point.
  • Top 10 all-time leaders in Free Throw percentage category earn 2 points.
  • Top 10 all-time leaders in Free Throw made category with FT% more than 85% earn 1 point.
  • Top 10 all-time leaders in Free Throw made per 36 minutes category with FT% more than 85% earn 1 point.

Calculate the points and we have the rank!


Generating plot…

AllTimeFTm <- NBA %>%
    group_by(Player) %>%
    summarise(FTm = sum(X3P),
              FTa = sum(X3PA),
              FT. = FTm / FTa) %>%
    filter(FT. > 0.85) %>%
    arrange(desc(FTm)) %>% head(10)
AllTimeFTm36 <- AllTimeFTma %>%
    filter(FT. >= 0.85) %>%
    arrange(desc(FTm_36), desc(FT.)) %>%
    head(n=10)
SuperFreeThrowers <- data.frame(Player = c(as.character(TopFT.$Player),
                               as.character(rep(head(AllTimeFT.$Player, 10), 2)),
                               as.character(AllTimeFTm$Player),
                               as.character(AllTimeFTm36$Player))) %>%
    count(Player) %>%
    arrange(desc(n))
# Display the final result:
SuperFreeThrowers %>%
    group_by(Player) %>%
    mutate(Pos = getmode(NBA$Position[NBA$Player %in% Player]),
           FTm = sum(NBA$FT[NBA$Player %in% Player]),
           FTa = sum(NBA$FTA[NBA$Player %in% Player]),
           "FT%" = round(FTm/FTa, 3),
           Tokens = paste(strrep("|", n)),
           TotalTokens = n) %>%
    select(-"n") %>%
    kable(escape = FALSE, caption = "The Most Prominent Free-Throwers") %>%
    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 Free-Throwers
Player Pos FTm FTa FT% Tokens TotalTokens
Rick Barry SF 3818 4243 0.900 |||||||| 8
Bill Sharman SG 3143 3559 0.883 ||||||| 7
Reggie Miller SG 6237 7026 0.888 ||||||| 7
Mark Price PG 2135 2362 0.904 ||||| 5
Stephen Curry PG 1993 2212 0.901 ||||| 5
Calvin Murphy PG 3445 3864 0.892 |||| 4
Peja Stojakovic SF 2237 2500 0.895 |||| 4
Steve Nash PG 3060 3384 0.904 |||| 4
Chauncey Billups PG 4496 5029 0.894 ||| 3
Dolph Schayes PF 6712 7904 0.849 ||| 3
Larry Bird SF 3960 4471 0.886 ||| 3
Chris Mullin SF 3616 4178 0.865 || 2
Ernie DiGregorio PG 461 511 0.902 || 2
Kevin Durant SF 4935 5596 0.882 || 2
Kyle Macy PG 717 821 0.873 || 2
Larry Costello PG 2432 2891 0.841 || 2
Larry Siegfried PG 1662 1945 0.854 || 2
Mahmoud Abdul-Rauf PG 1051 1161 0.905 || 2
Oscar Robertson PG 7694 9185 0.838 || 2
Ray Allen SG 4398 4920 0.894 || 2
Scott Skiles PG 1548 1741 0.889 || 2
Adrian Smith SG 2308 2762 0.836 | 1
Allan Houston SG 2572 2979 0.863 | 1
Alvin Sims SG 2 5 0.400 | 1
Bobby Wanzer SG 2179 2722 0.801 | 1
Brian Roberts PG 336 370 0.908 | 1
C.J. McCollum SG 529 627 0.844 | 1
Chet Walker SF 5079 6384 0.796 | 1
Coty Clarke SF 0 0 NaN | 1
Danilo Gallinari SF 1977 2292 0.863 | 1
Demetrius Jackson PG 3 6 0.500 | 1
Dirk Nowitzki PF 7104 8081 0.879 | 1
Eddy Curry C 1662 2588 0.642 | 1
Eric Anderson PF 16 27 0.593 | 1
Eric White SF 79 99 0.798 | 1
Flynn Robinson PG 1597 1881 0.849 | 1
Georgi Glouchkov PF 70 122 0.574 | 1
Greg Deane SG 5 7 0.714 | 1
Gundars Vetra SF 4 6 0.667 | 1
Isaiah Thomas PG 2119 2420 0.876 | 1
Ivano Newbill PF 45 104 0.433 | 1
Jack Marin SF 2405 2852 0.843 | 1
Jack Sikma C 4292 5053 0.849 | 1
Jamal Crawford SG 3499 4063 0.861 | 1
James Harden SG 4226 4951 0.854 | 1
Jeff Hornacek SG 2973 3390 0.877 | 1
Joe Fulks PF 1117 1446 0.772 | 1
Jose Calderon PG 892 1019 0.875 | 1
Kevin Martin SG 3561 4091 0.870 | 1
Kiki Vandeweghe SF 3484 3997 0.872 | 1
Kyle Korver SG 1138 1293 0.880 | 1
Magic Johnson PG 4960 5850 0.848 | 1
Max Zaslofsky SG 1469 1945 0.755 | 1
Micheal Williams PG 1545 1780 0.868 | 1
Ricky Pierce SG 3389 3871 0.875 | 1
Spud Webb PG 1946 2296 0.848 | 1


SuperFreeThrowers %>% wordcloud2(size=0.4, color='random-light', backgroundColor="black")
drawing





Free Throw Ratio & Rate

In this section, we will see the ratio and the rate of Free-Throw. The first one is similar to what I did previously, figuring out how many percent of the points earned from the Free Throw, and then sorted it by highest Free-Throw ratio.

NBA %>%
    filter(FT > 1) %>%
    group_by(Player) %>%
    summarise(Pos = getmode(Position),
              Team = getmode(Tm),
              Games = sum(G),
              Pts2 = sum(X2P) * 2,
              Pts3 = sum(X3P, na.rm = T) * 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),
              FTm = sum(FT),
              FTa = sum(FTA),
              FT. = round(FTm / FTa, 3)) %>%
    filter(FTm > 1200) %>%
    select(-c(FTm, FTa, Pts2:PtsFT)) %>%
    arrange(desc(PtsFTr)) %>%
    mutate(Player = reorder(Player, PtsFTr, 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 Free Throw Ratio") +
    coord_flip() +
    xlab("") +
    ylab("Shooting") +
    guides(fill = guide_legend(reverse = TRUE)) +
    theme(legend.position="bottom") +
    theme(legend.title=element_blank())


While in the 3-Points Ratio we will see the highest percentage points came from 3-Points is 79.3%, for Free Throw the highest is 39%, or about half. This is because in Free Throw each FT made a player only earn 1 point, contrast that to 3-points.

That’s why it’s a good idea to look at it from different angle, using data provided by this dataset, we are going to see the rate of free throws attempted per field goal attempted (FTr). And then, again, we sorted it by the FT rate.

In FT ratio above, we get the numbers from FT made and compared it with 2-points FG made times 2 and 3-points FG made times 3. In Free Throw rate, we take the number of Free Throw attempts in comparison with the number of Field Goal attempts (which is includes 3-Points FG), no multiplication on the number, each shooting attempt treated equal.


FTrateAVG <- NBA %>%
    summarise(Player = "Average",
              Games = sum(G, na.rm = T),
              FTa = sum(FTA, na.rm = T),
              FGa = sum(FGA, na.rm = T),
              FTapG = round(FTa / Games, 2),
              FGapG = round(FGa / Games, 2),
              FTr = round(FTa / FGa, 3)) %>%
    select(-c(Games, FGa, FTa))
FTrate <- 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),
              FTa = sum(FTA),
              FTr = round(FTa / FGa, 3),
              FTapG = round(FTa / Games, 2),
              FGapG = round(FGa / Games, 2)) %>%
    filter(FTa > 1200) %>%
    mutate(Rank = min_rank(desc(FTr))) %>%
    select(Rank, everything()) %>%
    arrange(Rank) %>%
    head(10) %>%
    bind_rows(FTrateAVG)
LabelFix <- FTrate %>%
    mutate(FTapG = FTapG,
           FTr = paste(FTr * 100, "%")) %>%
    gather(Parameter, Count, FTapG, -c(Player, FTr)) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(FTr))])))
FTrate %>%
    gather(Parameter, Count, FGapG:FTapG, -c(Player, FTr)) %>%
    mutate(Player = factor(Player, levels = unique(Player[order(desc(FTr))]))) %>%
    ggplot(aes(Player, Count, fill = Parameter, label = FTr)) +
    geom_bar(position="dodge", stat="identity") +
    geom_label(aes(label = FTr, y = 0.7),
              data = LabelFix,
              stat = 'identity',
              position = "stack",
              fill = "#FFFFFF",
              color = "#000000",
              size = 4) +
    ggtitle("Free Throw Rate") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") +
    ylab("FT/FG per game") +
    scale_fill_manual(name="", values=c("#0000FF", "#FF0000"),
                       labels=c("FG per Game", "FT per Game")) +
    theme(legend.position="bottom")

NA






End of Session


LS0tDQp0aXRsZTogJ1Zpc3VhbGl6aW5nIE5CQSBTZWFzb25zIFBhcnQgMzogU2hvb3RpbmdzJw0KYXV0aG9yOiAiTnVubm8gTnVncm9obyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjc3M6IHN0eWxlLmNzcw0KICAgIHRoZW1lOiBwYXBlcg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICBodG1sX2RvY3VtZW50Og0KICAgIGNzczogc3R5bGUuY3NzDQogICAgdGhlbWU6IHBhcGVyDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KPGJyLz4NCjxici8+DQoNCjxkaXYgY2xhc3M9ImhlYWRlciI+DQoNCjxjZW50ZXI+PGltZyBzcmM9ImltYWdlcy9OQkFMb2dvVHJhbnNwLnBuZyIgYWx0PSJkcmF3aW5nIiB3aWR0aD0iMjAwcHgiIGhlaWd0aD0iMTAwcHgiLz4NCg0KKipTZWFzb25zIDE5NTAtMjAxNyoqDQoNCjxkaXYgY2xhc3M9ImRyb3Bkb3duIj4NCg0KPGJ1dHRvbiBjbGFzcz0iZHJvcGJ0biI+UGFydCAzOiBTaG9vdGluZ3MgKEZHIGFuZCBGVCk8L2J1dHRvbj4NCg0KPGRpdiBjbGFzcz0iZHJvcGRvd24tY29udGVudCI+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zMCI+SW50cm9kdWN0aW9uICYgUHJlcGFyYXRpb248L2E+DQoNCjxhIGhyZWY9Imh0dHBzOi8vcnB1YnMuY29tL25pbmphenpsZS9OQkFTZWFzb25zMSI+UGFydCAxOiBQbGF5ZXJzPC9hPg0KDQo8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczIiPlBhcnQgMjogUG9pbnRzPC9hPg0KDQo8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczQiPlBhcnQgNDogU2hvb3RpbmdzICgzLVBvaW50cyBhbmQgTWl4ZWQpPC9hPg0KDQo8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczUiPlBhcnQgNTogU2tpbGxzPC9hPg0KDQo8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczYiPlBhcnQgNjogUm9sZXM8L2E+DQoNCjwvZGl2Pg0KDQo8L2Rpdj4NCg0KPC9jZW50ZXI+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KLS0tDQoNCjxici8+DQoNCiMgU2V0dXAgU3RhZ2UNCg0KPGJyLz4NCg0KTG9hZGluZyBuZWNlc3NhcnkgcGFja2FnZXMuDQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShnZ3JpZGdlcykNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkod29yZGNsb3VkMikNCmxpYnJhcnkoZm9yY2F0cykNCmBgYA0KDQo8YnIvPg0KDQpDcmVhdGUgYWRkaXRpb25hbCBmdW5jdGlvbnMNCg0KYGBge3J9DQojIE1vZGUgYXZlcmFnZQ0KZ2V0bW9kZSA8LSBmdW5jdGlvbih2KSB7DQogICB1bmlxdiA8LSB1bmlxdWUodikNCiAgIHVuaXF2W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh2LCB1bmlxdikpKV0NCn0NCmBgYA0KDQo8YnIvPg0KDQojIyMjIExvYWQgdGhlIGRhdGFzZXQNCg0KPGJyLz4NCg0KRGF0YXNldCB0YWtlbiBmcm9tOiBodHRwczovL3d3dy5rYWdnbGUuY29tL2RyZ2lsZXJtby9uYmEtcGxheWVycy1zdGF0cy92ZXJzaW9uLzINCg0KSWYgeW91IG5lZWQgdG8gc2VlIHRoZSBnbG9zc2FyeSBhbmQgdGhlIGRhdGEgdGlkeWluZyBwcm9jZXNzLCBwbGVhc2UgdmlzaXQgZmlyc3QgcGFydCBbaGVyZV0oaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnMwKS4NCg0KTG9hZGluZyBkYXRhc2V0cy4uLg0KDQpgYGB7cn0NCk5CQSA8LSByZWFkLmNzdigiTkJBX1RpZHlTZXQuY3N2IikNCk5CQV9TY2FsZWQgPC0gcmVhZC5jc3YoIk5CQV9TY2FsZWRfVGlkeVNldC5jc3YiKQ0KTkJBJFBvcyA8LSBmYWN0b3IoTkJBJFBvcywgbGV2ZWxzID0gYygiQyIsICJQRiIsICJTRiIsICJTRyIsICJQRyIpKQ0KUG9zQ29sb3JDb2RlIDwtIGMoIkMiPSIjRkYwMDAwIiwgIlBGIj0iI0ZGQTUwMCIsICJTRiI9IiNEREREMDAiICwiU0ciPSIjMDAwMEZGIiwgIlBHIj0iIzMyQ0QzMiIpDQpgYGANCg0KPGJyLz4NCg0KKipEaXNwbGF5aW5nIG1haW4gdGFibGUqKg0KDQo8ZGl2IGNsYXNzPSdtYWludGFibGUnPg0KDQpgYGB7cn0NCk5CQQ0KYGBgDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCi0tLQ0KDQo8YnIvPg0KDQojIyBTSE9PVElORw0KDQo8YnIvPg0KDQpJbiB0aGlzIHNlc3Npb24gd2UgZXhwbG9yZSBzaG9vdGluZyBzdGF0aXN0aWNzIG9mIHRoZSBOQkEgcGxheWVycy4gQXMgZGlzY3Vzc2VkIGluIHRoZSBbcHJldmlvdXMgc2Vzc2lvbl0oaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnMyKSwgdGhlcmUgYXJlIDMgYmFzaWMgc2hvb3RpbmcgY2F0ZWdvcnkgaW4gYmFza2V0YmFsbDogW0ZpZWxkIEdvYWxzIChGRyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZpZWxkX2dvYWxfKGJhc2tldGJhbGwpKSwgW1RocmVlLVBvaW50cyBGaWVsZCBHb2FsICgzUCldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RocmVlLXBvaW50X2ZpZWxkX2dvYWwpLCBhbmQgW0ZyZWUtVGhyb3cgKEZUKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRnJlZV90aHJvdykuIFdlIHdpbGwgYW5hbHl6ZSBpbi1kZXB0aCBlYWNoIG9uZSBvZiB0aGVtLg0KDQpTaW5jZSB0aGVyZSdzIHNpemUgbGltaXQgdG8gdXBsb2FkIHRvIHJwdWJzLCBJIG11c3QgZGl2aWRlIHRoaXMgc2hvb3RpbmcgY2F0ZWdvcnkgaW50byB0d28gc2VwZXJhdGVkIHNlc3Npb24uIEhlcmUgSSB3aWxsIG1ha2UgZXhwbG9yYXRvcnkgYW5hbHlzaXMgb24gdGhlIGdlbmVyYWwgc2hvb3Rpbmcgc3RhdHMgaW4gdGhlIGxlYWd1ZSwgRmllbGQgR09hbHMgKEZHKSBhbmQgRnJlZSBUaHJvd3MgKEZUKS4gMy1Qb2ludHMgYW5kIHRoZSBtaXhlZCBzaG9vdGluZyBzdGF0cyB3aWxsIGJlIGZvciB0aGUgW25leHQgc2Vzc2lvbl0oaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnM0KS4NCg0KQWdhaW4sIEkgdXNlIGJhc2ljIFJHQiBhcyBjb2xvci1jb2RlIGZvciBzaG9vdGluZyB0eXBlOiByZWQgZm9yIEZULCBncmVlbiBmb3IgM1AgYW5kIGJsdWUgZm9yIEZHLiANCg0KU3RhcnQgZnJvbSBhIHdpZGVyIHNjb3BlLCBsZXQncyBzZWUgdGhlIGdlbmVyYWwgc2hvb3RpbmcgY29tcGFyaXNvbiBpbiB0aGUgbGVhZ3VlLg0KDQoqKk5PVEU6IFdoaWxlIEkgZG9uJ3QgaGF2ZSB0aW1lIG9yIGFuIGluc3BpcmF0aW9uIGFib3V0IHRoZSBuYXJyYXRpdmUsIFtMb3JlbSBpcHN1bSB0ZXh0c10oaHR0cHM6Ly93d3cubGlwc3VtLmNvbS8pIHdpbGwgYmUgaW4gZGlzcGxheS4qKg0KDQo8YnIvPg0KDQojIyMgU2hvb3RpbmcgUGVyY2VudGFnZSBEZW5zaXR5DQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQuIFNlZCBhYyByaG9uY3VzIGlwc3VtLiBOYW0gYWxpcXVhbSBiaWJlbmR1bSBiaWJlbmR1bS4gVmVzdGlidWx1bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBmYXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEgQ3VyYWU7IE51bGxhIHRpbmNpZHVudCBtZXR1cyBxdWlzIG1hZ25hIHBvc3VlcmUgZXVpc21vZC4gTmFtIGxhb3JlZXQgbW9sbGlzIGZldWdpYXQuIFV0IHJob25jdXMgbGliZXJvIGEgaXBzdW0gcnV0cnVtIGN1cnN1cy4gRXRpYW0gZWxlaWZlbmQgZmF1Y2lidXMgaW50ZXJkdW0uDQoNCjxkaXYgY2xhc3M9InRhYmxlIj4NCg0KYGBge3J9DQpOQkEgJT4lDQogICAgZ3JvdXBfYnkoUG9zKSAlPiUNCiAgICBzdW1tYXJpc2UoRkcybSA9IHN1bShYMlAsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGRzJhID0gc3VtKFgyUEEsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGRzIuID0gcm91bmQoRkcybS9GRzJhLCAzKSwNCiAgICAgICAgICAgICAgRkczbSA9IHN1bShYM1AsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGRzNhID0gc3VtKFgzUEEsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGRzMuID0gcm91bmQoRkczbS9GRzNhLCAzKSwNCiAgICAgICAgICAgICAgRlRtID0gc3VtKEZULCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRlRhID0gc3VtKEZUQSwgbmEucm09VCksDQogICAgICAgICAgICAgIEZULiA9IHJvdW5kKEZUbS9GVGEsIDMpKSAlPiUNCiAgICBzZWxlY3QoLWMoRkcybTpGRzJhLCBGRzNtOkZHM2EsIEZUbTpGVGEpKSAlPiUNCiAgICBtdXRhdGUoUG9zID0gY2VsbF9zcGVjKFBvcywgY29sb3IgPSAid2hpdGUiLCBhbGlnbiA9ICJjIiwgDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSBmYWN0b3IoUG9zLCBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc0NvbG9yQ29kZSkpKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgYWxpZ24gPSAiYyIsIGNhcHRpb24gPSAiQXZlcmFnZSBTaG9vdGluZyBQZXJjZW50YWdlcyBieSBQb3NpdGlvbiIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAiZmxvYXRfbGVmdCIpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDE6NCwgd2lkdGggPSAiNWVtIikNCmBgYA0KPGJyLz4NCjxici8+DQoNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQuIENyYXMgc2l0IGFtZXQgbWF1cmlzIGluIGV4IHVsdHJpY2llcyBlbGVtZW50dW0gdmVsIHJ1dHJ1bSBkb2xvci4gUGhhc2VsbHVzIHRlbXBvciBjb252YWxsaXMgZHVpLCBpbiBoZW5kcmVyaXQgbWF1cmlzIHBsYWNlcmF0IHNjZWxlcmlzcXVlLiBNYWVjZW5hcyBhIGFjY3Vtc2FuIGVuaW0sIGEgbWF4aW11cyB2ZWxpdC4gUGVsbGVudGVzcXVlIGluIHJpc3VzIGVnZXQgZXN0IGZhdWNpYnVzIGNvbnZhbGxpcyBuZWMgYXQgbnVsbGEuIFBoYXNlbGx1cyBuZWMgbGFjaW5pYSBqdXN0by4gTW9yYmkgZmVybWVudHVtLCBvcmNpIGlkIHZhcml1cyBhY2N1bXNhbiwgbmliaCBuZXF1ZSBwb3J0dGl0b3IgaXBzdW0sIGNvbnNlY3RldHVyIGx1Y3R1cyByaXN1cyBhcmN1IGFjIGV4Lg0KDQpTdXNwZW5kaXNzZSBldCBhdWN0b3IgbmlzbC4gU3VzcGVuZGlzc2UgY3Vyc3VzIHVsdHJpY2VzIHF1YW0gbm9uIHZ1bHB1dGF0ZS4gUGhhc2VsbHVzIGV0IHBoYXJldHJhIG5lcXVlLCB2ZWwgZmV1Z2lhdCBlcmF0LiBTZWQgZmV1Z2lhdCBlbGl0IGF0IG1hdXJpcyBjb21tb2RvIGNvbnNlcXVhdC4gU2VkIGNvbmd1ZSBsZWN0dXMgaWQgbWF0dGlzIGhlbmRyZXJpdC4gTWF1cmlzIHR1cnBpcyBuaXNsLCBjb25ndWUgZWdldCB2ZWxpdCBzZWQsIGltcGVyZGlldCBjb252YWxsaXMgbWFnbmEuDQoNCjwvZGl2Pg0KDQpOb24gc29kYWxlcyBuZXF1ZSBzb2RhbGVzIHV0LiBHcmF2aWRhIGN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMuIE5pYmggY3JhcyBwdWx2aW5hciBtYXR0aXMgbnVuYyBzZWQgYmxhbmRpdCBsaWJlcm8gdm9sdXRwYXQgc2VkLg0KDQpgYGB7ciBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNywgd2FybmluZz1GQUxTRX0NCk5CQSAlPiUNCiAgICBncm91cF9ieShQb3MsIFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKEZHMm0gPSBzdW0oWDJQLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkcyYSA9IHN1bShYMlBBLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkcyLiA9IHJvdW5kKEZHMm0vRkcyYSwgMyksDQogICAgICAgICAgICAgIEZHM20gPSBzdW0oWDNQLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkczYSA9IHN1bShYM1BBLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkczLiA9IHJvdW5kKEZHM20vRkczYSwgMyksDQogICAgICAgICAgICAgIEZUbSA9IHN1bShGVCwgbmEucm09VCksDQogICAgICAgICAgICAgIEZUYSA9IHN1bShGVEEsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGVC4gPSByb3VuZChGVG0vRlRhLCAzKSkgJT4lDQogICAgc2VsZWN0KC1jKEZHMm06RkcyYSwgRkczbTpGRzNhLCBGVG06RlRhKSkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgUGN0LCBGRzIuOkZULiwgLWMoUG9zLCBQbGF5ZXIpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKFBjdCwgUG9zLCBmaWxsPVBhcmFtZXRlcikpICsNCiAgICBnZ3RpdGxlKCJTaG9vdGluZyBQZXJjZW50YWdlIERlbnNpdHkgYnkgUG9zaXRpb24iKSArDQogICAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYT0wLjI1KSArDQogICAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAuMDEsIDApLCBsaW1pdHMgPSByZXYobGV2ZWxzKE5CQSRQb3MpKSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwoIlBhcmFtZXRlciIsIHZhbHVlcyA9IGMoImJsdWUiLCAiZ3JlZW4iLCAicmVkIikpICsNCiAgICB4bGFiKCJQZXJjZW50YWdlIikgKw0KICAgIHlsYWIoIlBvc2l0aW9uIikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikNCmBgYA0KDQo8YnIvPg0KPGJyLz4NCg0KPC9kaXY+DQoNCjxici8+DQoNCiMjIyBOQkEgU2hvb3RpbmcgYnkgWWVhcg0KDQo8ZGl2IGNsYXNzPSJCb3giPg0KDQo8YnIvPg0KDQpJbiBkaWN0dW0gZXggbGlndWxhLCBuZWMgbHVjdHVzIGVzdCBiaWJlbmR1bSB1dC4gRG9uZWMgdG9ydG9yIG5lcXVlLCB0aW5jaWR1bnQgc2l0IGFtZXQgZmV1Z2lhdCBpbiwgZWdlc3RhcyBuZWMgb3JjaS4gQWVuZWFuIG9ybmFyZSBldSBsZWN0dXMgZXQgdWx0cmljaWVzLiBWaXZhbXVzIHN1c2NpcGl0IHRpbmNpZHVudCBzYXBpZW4sIHV0IG1vbGxpcyBwdXJ1cyBzb2RhbGVzIGFjLiBTdXNwZW5kaXNzZSBwb3RlbnRpLiBOdW5jIGVnZXQgc2FwaWVuIGlkIGxhY3VzIG1vbGVzdGllIHNvbGxpY2l0dWRpbiBldSB2aXRhZSB1cm5hLiBFdGlhbSBhIHBvcnRhIGFudGUuIE51bGxhbSBsYW9yZWV0IHRvcnRvciBpbiBtYXVyaXMgaGVuZHJlcml0IGFsaXF1ZXQgYWMgdmVsIGVyYXQuIFByb2luIG9ybmFyZSBtYWduYSBpbiBlbGVtZW50dW0gZmV1Z2lhdC4gUHJhZXNlbnQgbm9uIGxhb3JlZXQgc2VtLg0KDQoNCmBgYHtyIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpOQkEgJT4lDQogICAgZ3JvdXBfYnkoWWVhcikgJT4lDQogICAgc3VtbWFyaXNlKEZHMm0gPSBzdW0oWDJQLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkcyYSA9IHN1bShYMlBBLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkcyLiA9IHJvdW5kKEZHMm0vRkcyYSwgMyksDQogICAgICAgICAgICAgIEZHM20gPSBzdW0oWDNQLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkczYSA9IHN1bShYM1BBLCBuYS5ybT1UKSwNCiAgICAgICAgICAgICAgRkczLiA9IHJvdW5kKEZHM20vRkczYSwgMyksDQogICAgICAgICAgICAgIEZUbSA9IHN1bShGVCwgbmEucm09VCksDQogICAgICAgICAgICAgIEZUYSA9IHN1bShGVEEsIG5hLnJtPVQpLA0KICAgICAgICAgICAgICBGVC4gPSByb3VuZChGVG0vRlRhLCAzKSkgJT4lDQogICAgc2VsZWN0KC1jKEZHMm06RkcyYSwgRkczbTpGRzNhLCBGVG06RlRhKSkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgVmFsdWUsIEZHMi46RlQuLCAtWWVhcikgJT4lDQogICAgZ2dwbG90KCkgKw0KICAgIGdlb21fbGluZShhZXMoWWVhciwgVmFsdWUsIGdyb3VwPVBhcmFtZXRlciwgY29sb3I9UGFyYW1ldGVyKSwgc2l6ZSA9IDEuMiwgYWxwaGEgPSAxKSArDQogICAgZ2d0aXRsZSgiU2hvb3RpbmcgUGVyY2VudGFnZSBieSBZZWFyIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk1MCwgMjAyMCwgMTApKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKCJQYXJhbWV0ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJibHVlIiwgImdyZWVuIiwgInJlZCIpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJGRzIlIiwgIkZHMyUiLCAiRlQlIikpICsNCiAgICBndWlkZXMoZ3JvdXA9RkFMU0UpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQpgYGANCg0KPGJyLz4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQojIyMgSGl0L01pc3NlZCBTaG9vdHMgYnkgUG9zaXRpb24NCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KTnVsbGEgZmFjaWxpc2kuIFV0IHV0IGV1aXNtb2QgZXJvcy4gTnVsbGFtIGF0IHR1cnBpcyByaG9uY3VzIGZlbGlzIHVsdHJpY2VzIHVsdHJpY2VzLiBNYXVyaXMgc2VkIGV4IHNpdCBhbWV0IHRvcnRvciBldWlzbW9kIGVsZW1lbnR1bS4gTW9yYmkgbmVjIHJpc3VzIGV0IGxhY3VzIGFjY3Vtc2FuIGZpbmlidXMuIFByYWVzZW50IGVnZXN0YXMgbG9ib3J0aXMgcHVydXMgbm9uIGRpY3R1bS4gVmVzdGlidWx1bSB2dWxwdXRhdGUgdHVycGlzIHV0IHZlaGljdWxhIGZpbmlidXMuIE51bGxhIGZhY2lsaXNpLiBDdXJhYml0dXIgZGFwaWJ1cyBudWxsYSBzaXQgYW1ldCBwbGFjZXJhdCBjb25zZWN0ZXR1ci4gTWF1cmlzIGVnZXQgdG9ydG9yIHVsdHJpY2VzLCBtYXR0aXMgbnVuYyBub24sIHBvcnRhIHVybmEuIER1aXMgaWQgYXVndWUgb3JuYXJlLCBjb252YWxsaXMgbnVsbGEgZWZmaWNpdHVyLCBjb25ndWUgaXBzdW0uDQoNCmBgYHtyIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQ9NC41fQ0KTkJBICU+JQ0KICAgIGdyb3VwX2J5KFBvcykgJT4lDQogICAgc3VtbWFyaXNlKEZHMm0gPSBzdW0oWDJQLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGRzJhID0gc3VtKFgyUEEsIG5hLnJtID0gVCkgLSBGRzJtLA0KICAgICAgICAgICAgICBGRzNtID0gc3VtKFgzUCwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgICAgRkczYSA9IHN1bShYM1BBLCBuYS5ybSA9IFQpIC0gRkczbSwNCiAgICAgICAgICAgICAgRlRtID0gc3VtKEZULCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBLCBuYS5ybSA9IFQpIC0gRlRtKSAlPiUNCiAgICBnYXRoZXIoUGFyYW1ldGVyLCBDb3VudCwgRkcybTpGVGEsIC1Qb3MpICU+JQ0KICAgIG11dGF0ZShTaG9vdFR5cGUgPSBhcy5mYWN0b3IoaWZlbHNlKFBhcmFtZXRlciAlaW4lIGMoIkZHMm0iLCAiRkcyYSIpLCAiRkcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGFyYW1ldGVyICVpbiUgYygiRkczbSIsICJGRzNhIiksICJGRzMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRlQiKSkpKSAlPiUNCiAgICBncm91cF9ieShQb3MsIFNob290VHlwZSkgJT4lDQogICAgZ2dwbG90KGFlcyhTaG9vdFR5cGUsIENvdW50LCBmaWxsID0gUGFyYW1ldGVyKSkgKyANCiAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnc3RhY2snKSArDQogICAgZmFjZXRfZ3JpZCh+IFBvcykgKw0KICAgIGdndGl0bGUoIkhpdC9NaXNzZWQgU2hvb3RzIGJ5IFBvc2l0aW9uIikgKw0KICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM3RjdGRkYiLCAiIzAwMDBGRiIsICIjNjZCMjY2IiwgIiMwMDgwMDAiLCAiI0ZGNEM0QyIsIiNGRjAwMDAiKSkNCmBgYA0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyBTaG9vdGluZyBIaXQtTWlzcyBieSBZZWFyDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCk1hZWNlbmFzIGV0IHZlbGl0IHRyaXN0aXF1ZSwgdWxsYW1jb3JwZXIgbWFzc2EgaW4sIHNlbXBlciBuaWJoLiBWaXZhbXVzIHNvZGFsZXMsIHNlbSB2ZWwgdmVuZW5hdGlzIHNjZWxlcmlzcXVlLCBuZXF1ZSBtZXR1cyBlZmZpY2l0dXIgdmVsaXQsIHZpdGFlIGdyYXZpZGEgbWFnbmEgbWV0dXMgc2l0IGFtZXQgbWFnbmEuIER1aXMgc2VtcGVyIHJob25jdXMgZG9sb3IsIGluIHRpbmNpZHVudCBudW5jIGFjY3Vtc2FuIHJ1dHJ1bS4gUGhhc2VsbHVzIGV1IGxpZ3VsYSB2YXJpdXMgbnVsbGEgdWx0cmljaWVzIHBvcnRhLiBQcm9pbiBsYW9yZWV0IHBlbGxlbnRlc3F1ZSBzZW0gcXVpcyBwbGFjZXJhdC4gSW4gcnV0cnVtIG1hZ25hIHV0IGFudGUgaWFjdWxpcywgc2l0IGFtZXQgdGVtcG9yIHZlbGl0IG1vbGxpcy4gTnVsbGEgY29uc2VxdWF0IGltcGVyZGlldCBtb2xsaXMuIE1hdXJpcyB2ZXN0aWJ1bHVtIHF1YW0gcXVpcyBlcm9zIHJ1dHJ1bSBwaGFyZXRyYS4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIFBoYXNlbGx1cyBjb252YWxsaXMgZXJhdCBzZWQgYmxhbmRpdCBibGFuZGl0LiBTZWQgcHJldGl1bSB2aXRhZSBwdXJ1cyBzZWQgdGVtcHVzLiBEb25lYyB1bGxhbWNvcnBlciBuZWMgZW5pbSBhdCB2aXZlcnJhLiBOdWxsYW0gdnVscHV0YXRlIHNpdCBhbWV0IHVybmEgdml0YWUgc2NlbGVyaXNxdWUuIE51bmMgaWQgY29uZ3VlIGVsaXQuIFByYWVzZW50IG5lYyB2ZWxpdCB1dCBkb2xvciBsdWN0dXMgdmVuZW5hdGlzIHV0IGVnZXQgZHVpLiBNYXVyaXMgcGVsbGVudGVzcXVlIHBlbGxlbnRlc3F1ZSBjb25zZWN0ZXR1ci4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NC41fQ0KSGl0TWlzc0ZHMiA8LSBOQkEgJT4lIGdyb3VwX2J5KFllYXIpICU+JQ0KICAgIHN1bW1hcmlzZShTaG9vdFR5cGUgPSAiRkcyIiwNCiAgICAgICAgICAgICAgSGl0ID0gc3VtKFgyUCwgbmEucm0gPSBUKSAvIHN1bShHKSwNCiAgICAgICAgICAgICAgTWlzcyA9IHN1bShYMlBBLCBuYS5ybSA9IFQpIC8gc3VtKEcpKQ0KSGl0TWlzc0ZHMyA8LSBOQkEgJT4lIGdyb3VwX2J5KFllYXIpICU+JQ0KICAgIHN1bW1hcmlzZShTaG9vdFR5cGUgPSAiRkczIiwNCiAgICAgICAgICAgICAgSGl0ID0gc3VtKFgzUCwgbmEucm0gPSBUKSAvIHN1bShHKSwNCiAgICAgICAgICAgICAgTWlzcyA9IHN1bShYM1BBLCBuYS5ybSA9IFQpIC8gc3VtKEcpKQ0KSGl0TWlzc0ZUIDwtIE5CQSAlPiUgZ3JvdXBfYnkoWWVhcikgJT4lDQogICAgc3VtbWFyaXNlKFNob290VHlwZSA9ICJGVCIsDQogICAgICAgICAgICAgIEhpdCA9IHN1bShGVCwgbmEucm0gPSBUKSAvIHN1bShHKSwNCiAgICAgICAgICAgICAgTWlzcyA9IHN1bShGVEEsIG5hLnJtID0gVCkgLyBzdW0oRykpDQpIaXRNaXNzU2hvdCA8LSByYmluZChIaXRNaXNzRkcyLCBIaXRNaXNzRkczLCBIaXRNaXNzRlQpDQoNCkhpdE1pc3NTaG90ICU+JQ0KICAgIGdncGxvdChhZXMoeD1ZZWFyLA0KICAgICAgICAgICAgICAgeT1IaXQsDQogICAgICAgICAgICAgICB5bWluPUhpdCwNCiAgICAgICAgICAgICAgIHltYXg9TWlzcywNCiAgICAgICAgICAgICAgIGZpbGw9U2hvb3RUeXBlLA0KICAgICAgICAgICAgICAgY29sb3I9U2hvb3RUeXBlKSkgKyANCiAgICBnZW9tX2xpbmUoc2l6ZT0yKSArIA0KICAgIGdlb21fcmliYm9uKGFscGhhPTAuMykgKw0KICAgIHlsYWIoIkhpdCAvIE1pc3NlZCBTaG90cyIpICsNCiAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwobmFtZSA9ICIiLCB2YWx1ZXMgPSBjKDEsIDEsIDEpKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjMDAwMEZGIiwgIiMwMDgwMDAiLCAiI0ZGMDAwMCIpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KYGBgDQoNClRoZSB0aGljayBsaW5lIHNob3dzIHNob290aW5nIG1hZGUgYW5kIHRoZSB0aGluIGxpbmUgc2hvd3MgdGhlIGF0dGVtcHRzLg0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCi0tLQ0KDQo8YnIvPg0KPGJyLz4NCg0KIyMgRmllbGQgR29hbHMoRkcpDQoNCjxici8+DQoNCkZpZWxkIEdvYWxzIGlzIGFueXRoaW5nIG90aGVyIHRoYW4gRnJlZSBUaHJvd3MgdGhhdCBjb252ZXJ0IHRvIHBvaW50cy4gVGhhdCBtZWFucyAzLXBvaW50cyBGRyBhbHNvIGluY2x1ZGVkIGluIHRoZSBGaWVsZCBHb2FscyBzdGF0cy4gSG93ZXZlciwgdGhpcyBkb2Vzbid0IG5lY2Vzc2FyaWx5IG1lYXN1cmUgJ3Nob290aW5nJyBza2lsbCBwZXIgc2UsIHNpbmNlIGl0IGFsc28gaW5jbHVkZWQgb3RoZXIgbWV0aG9kIG9mIHNjb3Jpbmcgc3VjaCBzbGFtLWR1bmssIGxheS11cCwgZXZlbiBhY2NpZGVudGFsIHNjb3JpbmcuDQoNCiMjIyBGaWVsZCBHb2FscyAlIEFubnVhbCBMZWFndWUgTGVhZGVycw0KDQo8ZGl2IGNsYXNzPSJCb3giPg0KDQo8YnIvPg0KDQpMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LCBzZWQgZG8gZWl1c21vZCB0ZW1wb3IgaW5jaWRpZHVudCB1dCBsYWJvcmUgZXQgZG9sb3JlIG1hZ25hIGFsaXF1YS4gQWMgZmVsaXMgZG9uZWMgZXQgb2RpbyBwZWxsZW50ZXNxdWUgZGlhbSB2b2x1dHBhdC4gRGljdHVtIHZhcml1cyBkdWlzIGF0IGNvbnNlY3RldHVyIGxvcmVtLiBWZW5lbmF0aXMgbGVjdHVzIG1hZ25hIGZyaW5naWxsYSB1cm5hIHBvcnR0aXRvciByaG9uY3VzIGRvbG9yIHB1cnVzIG5vbi4NCg0KPGRpdiBjbGFzcz0idGFibGUiPg0KYGBge3J9DQpUb3BGRy4gPC0gTkJBICU+JQ0KICAgIGZpbHRlcihGRy4gPiAwKSAlPiUNCiAgICBncm91cF9ieShZZWFyLCBQbGF5ZXIpICU+JQ0KICAgIHN1bW1hcmlzZShQb3MgPSBnZXRtb2RlKFBvcyksDQogICAgICAgICAgICAgIFRlYW0gPSBnZXRtb2RlKFRtKSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBzdW0oRyksDQogICAgICAgICAgICAgIE1QRyA9IHJvdW5kKHN1bShNUCkvR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBQUEcgPSByb3VuZChzdW0oUFRTKS9HYW1lcywgMSksDQogICAgICAgICAgICAgIEZHbSA9IHN1bShGRyksDQogICAgICAgICAgICAgIEZHYSA9IHN1bShGR0EpLA0KICAgICAgICAgICAgICBGRy4gPSByb3VuZChGR20vRkdhLCAzKSkgJT4lDQogICAgZmlsdGVyKGlmZWxzZShZZWFyICVpbiUgYygxOTUwOjE5NTEsIDE5NjEtMTk2MiksIEZHbSA+PSAyMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgYygxOTUyOjE5NTUsIDE5NjMtMTk2NCwgMTk2NiksIEZHbSA+PSAyMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTU2OjE5NTksIEZHbSA+PSAyMzAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoWWVhciAlaW4lIDE5NjA6MTk2MSwgRkdtID49IDE5MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSBjKDE5NjUsIDE5Njc6MTk2OCksIEZHbSA+PSAyMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTY5LCBGR20gPj0gMjMwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTcwLCBHYW1lcyA+PSA3MCAmIEZHYSA+PSA3MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk3MToxOTcyLCBGR2EgPj0gNzAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk3MzoxOTc0LCBGR2EgPj0gNTYwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTk5LCBGR20gPj0gMTgzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoWWVhciAlaW4lIDIwMTIsIEZHbSA+PSAyNDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkdtID49IDMwMCkpKSkpKSkpKSkpKSAlPiUNCiAgICBncm91cF9ieShZZWFyKSAlPiUNCiAgICBzdW1tYXJpc2UoUGxheWVyID0gUGxheWVyW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgVGVhbSA9IFRlYW1bd2hpY2gubWF4KEZHLildLA0KICAgICAgICAgICAgICBQb3MgPSBQb3Nbd2hpY2gubWF4KEZHLildLA0KICAgICAgICAgICAgICBHYW1lcyA9IEdhbWVzW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgTVBHID0gTVBHW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgUFBHID0gUFBHW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkdtID0gRkdtW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkdhID0gRkdhW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkcuID0gbWF4KEZHLiwgbmEucm0gPSBUKSkgJT4lDQogICAgYXJyYW5nZShkZXNjKFllYXIpKQ0KDQpUb3BGRy4gICU+JQ0KICAgIG11dGF0ZShQb3MgPSBjZWxsX3NwZWMoUG9zLCBjb2xvciA9ICJ3aGl0ZSIsIGFsaWduID0gImMiLCANCiAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IGZhY3RvcihQb3MsIGMoIkMiLCAiUEYiLCAiU0YiLCAiU0ciLCAiUEciKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zQ29sb3JDb2RlKSkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBjYXB0aW9uID0gIkFubnVhbCBMZWFndWUgTGVhZGVycyBGaWVsZCBHb2FscyAlIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsICJob3ZlciIsIGZ1bGxfd2lkdGggPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoMiwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEwLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCipUbyBxdWFsaWZ5IGZvciBsZWFndWUgbGVhZGVycywgYSBwbGF5ZXIgbXVzdCBtZWV0IHRoZSBbUmF0ZSBTdGF0aXN0aWMgUmVxdWlyZW1lbnRzXShodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vYWJvdXQvcmF0ZV9zdGF0X3JlcS5odG1sKSoNCg0KPC9kaXY+DQoNCjxici8+DQoNCkdlbmVyYXRpbmcgcGxvdC4uLg0KYGBge3Igd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSA5fQ0KbWVhbkZHLlllYXIgPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFllYXIpICU+JQ0KICAgIGZpbHRlcihGRy4gPiAwKSAlPiUNCiAgICBzdW1tYXJpc2UoRkcuID0gc3VtKEZHKS9zdW0oRkdBKSkNCg0KVEZHLiA8LSBUb3BGRy4gJT4lDQogICAgZ2dwbG90KCkgKw0KICAgIGdlb21fYmFyKGFlcyhZZWFyLCBGRy4sIGZpbGwgPSBQb3MsIHRleHQgPSBwYXN0ZSgiUGxheWVyOiIsIFBsYXllcikpLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICAgIGdlb21fbGluZShkYXRhPW1lYW5GRy5ZZWFyLCBhZXMoWWVhciwgRkcuLCBsaW5ldHlwZSA9ICJBdmVyYWdlIGxpbmUiKSwgY29sb3IgPSAiYmxhY2siKSArDQogICAgZ2d0aXRsZSgiQW5udWFsIEZpZWxkIEdvYWwgUGVyY2VudGFnZSBMZWFkZXJzIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oVG9wRkcuJEZHLiksIGNvbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5NTAsIDIwMTcsIDEwKSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKCJQb3MiLCB2YWx1ZXMgPSBQb3NDb2xvckNvZGUpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQ0KDQpwVEZHIDwtIGdncGxvdGx5KFRGRy4pDQoNCiNhcGlfY3JlYXRlKHBURkcsIGZpbGVuYW1lID0gIkFubnVhbEZHLkxlYWRlcnMiKQ0KYGBgDQoNCjxkaXY+PGEgaHJlZj0iaHR0cHM6Ly9wbG90Lmx5L35OdW5Ob0RlQ2FidW5pYy8xOC8/c2hhcmVfa2V5PTZwUUV1Z0lxdmMzNFk1Rm4zSXJCR2UiIHRhcmdldD0iX2JsYW5rIiB0aXRsZT0iQW5udWFsRkcuTGVhZGVycyIgc3R5bGU9ImRpc3BsYXk6IGJsb2NrOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48aW1nIHNyYz0iaHR0cHM6Ly9wbG90Lmx5L35OdW5Ob0RlQ2FidW5pYy8xOC5wbmc/c2hhcmVfa2V5PTZwUUV1Z0lxdmMzNFk1Rm4zSXJCR2UiIGFsdD0iQW5udWFsRkcuTGVhZGVycyIgc3R5bGU9Im1heC13aWR0aDogMTAwJTt3aWR0aDogODgwcHg7IiAgd2lkdGg9Ijg4MCIgb25lcnJvcj0idGhpcy5vbmVycm9yPW51bGw7dGhpcy5zcmM9J2h0dHBzOi8vcGxvdC5seS80MDQucG5nJzsiIC8+PC9hPjxzY3JpcHQgZGF0YS1wbG90bHk9Ik51bk5vRGVDYWJ1bmljOjE4IiBzaGFyZWtleS1wbG90bHk9IjZwUUV1Z0lxdmMzNFk1Rm4zSXJCR2UiIHNyYz0iaHR0cHM6Ly9wbG90Lmx5L2VtYmVkLmpzIiBhc3luYz48L3NjcmlwdD48L2Rpdj4NCg0KKmNsaWNrIGltYWdlIGZvciBpbnRlcmFjdGl2ZSBwbG90bHkgZ3JhcGgqDQo8YnIvPg0KDQo8ZGl2IGNsYXNzPSJGYWN0Ij4NCg0KPHVsIGNsYXNzPSJDdXN0b21MaXN0Ij4NCg0KPGxpPkF2ZXJhZ2UgdG9wIEZHJSBpcyBgciByb3VuZChzdW0oVG9wRkcuJEZHbSkvc3VtKFRvcEZHLiRGR2EpLCAzKWAuPC9saT4NCjxsaT5IaWdoZXN0IHRvcCBGRyUgaXMgYHIgbWF4KFRvcEZHLiRGRy4pYCwgaW4gc2Vhc29uIGByIFRvcEZHLiRZZWFyW3doaWNoLm1heChUb3BGRy4kRkcuKV1gLjwvbGk+DQo8bGk+TG93ZXN0IHRvcCBGRyUgaXMgYHIgbWluKFRvcEZHLiRGRy4pYCwgaW4gc2Vhc29uIGByIFRvcEZHLiRZZWFyW3doaWNoLm1pbihUb3BGRy4kRkcuKV1gLjwvbGk+DQo8bGk+VGhlIDxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0ZGMDAwMDt0ZXh0LWFsaWduOiBjOyIgPkM8L3NwYW4+ZW50ZXJzIGRvbWluYXRlZCB0aGUgbGVhZ3VlIGJ5IGByIHJvdW5kKG5yb3coVG9wRkcuW1RvcEZHLiRQb3MgPT0gIkMiLF0pL25yb3coVG9wRkcuKSoxMDAsIDEpYCUsIGZvbGxvd2VkIGJ5IDxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0ZGQTUwMDt0ZXh0LWFsaWduOiBjOyIgPlBGPC9zcGFuPiB3aXRoIGByIHJvdW5kKG5yb3coVG9wRkcuW1RvcEZHLiRQb3MgPT0gIlBGIixdKS9ucm93KFRvcEZHLikqMTAwLCAxKWAlLCB0aGVuIDxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0REREQwMDt0ZXh0LWFsaWduOiBjOyIgPlNGPC9zcGFuPjxici8+PHNwYW4+IGNvbXBsZXRlIHRoZSBsZWFkZXJib2FyZCB3aXRoIGByIHJvdW5kKG5yb3coVG9wRkcuW1RvcEZHLiRQb3MgPT0gIlNGIixdKS9ucm93KFRvcEZHLikqMTAwLCAxKWAlLiBObyBndWFyZCBldmVyIGJlIGluIEZHJSBsZWFkZXJib2FyZCwgdGhpcyBiZWNhdXNlIGd1YXJkcyB0ZXJyaXRvcnkgaXMgb3V0c2lkZSwgd2hpbGUgcG9zdCBwbGF5ZXJzIChDIC8gUEYpIHBvc2l0aW9uZWQgbmVhciB0aGUgYmFza2V0IGhhdmUgZ3JlYXRlciBjaGFuY2Ugb2Ygc3VjY2VzcyBpbiBkZWxpdmVyaW5nIHRoZSBiYWxsIHRvIHRoZSBiYXNrZXQuPC9saT4NCiAgICANCjwvdWw+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyBBbGwtVGltZSBGaWVsZCBHb2FscyAlIExlYWRlcnMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KUXVpcyBlbGVpZmVuZCBxdWFtIGFkaXBpc2Npbmcgdml0YWUgcHJvaW4gc2FnaXR0aXMuIE1hZ25hIGZlcm1lbnR1bSBpYWN1bGlzIGV1IG5vbiBkaWFtIHBoYXNlbGx1cyB2ZXN0aWJ1bHVtLiBQaGFyZXRyYSBldCB1bHRyaWNlcyBuZXF1ZSBvcm5hcmUgYWVuZWFuLiBDdXJzdXMgbWF0dGlzIG1vbGVzdGllIGEgaWFjdWxpcyBhdCBlcmF0LiBFbGVtZW50dW0gcHVsdmluYXIgZXRpYW0gbm9uIHF1YW0gbGFjdXMgc3VzcGVuZGlzc2UgZmF1Y2lidXMuIFBsYWNlcmF0IG9yY2kgbnVsbGEgcGVsbGVudGVzcXVlIGRpZ25pc3NpbSBlbmltIHNpdCBhbWV0Lg0KDQo8ZGl2IGNsYXNzPSJ0YWJsZSI+DQoNCmBgYHtyfQ0KQWxsVGltZUZHLiA8LSBOQkEgJT4lDQogICAgZmlsdGVyKEZHLiA+IDApICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBBY3RpdmVZZWFycyA9IHBhc3RlKGdldG1vZGUoWWVhclN0YXJ0KSwgIi0iLCBnZXRtb2RlKFllYXJFbmQpKSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBzdW0oRyksDQogICAgICAgICAgICAgIE1QRyA9IHJvdW5kKHN1bShNUCkgLyBHYW1lcywgMSksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKHN1bShQVFMpIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBGR20gPSBzdW0oRkcpLA0KICAgICAgICAgICAgICBGR2EgPSBzdW0oRkdBKSwNCiAgICAgICAgICAgICAgRkcuID0gcm91bmQoRkdtL0ZHYSwgMykpICU+JQ0KICAgIGZpbHRlcihGR20gPj0gMjAwMCkgJT4lDQogICAgYXJyYW5nZShkZXNjKEZHLiksIGRlc2MoRkdtKSkgJT4lDQogICAgbXV0YXRlKFJhbmsgPSBtaW5fcmFuayhkZXNjKEZHLikpKSAlPiUNCiAgICBzZWxlY3QoUmFuaywgZXZlcnl0aGluZygpKQ0KDQpBbGxUaW1lRkcuICU+JQ0KICAgIGhlYWQobj0xMCkgJT4lDQogICAgbXV0YXRlKFBvcyA9IGNlbGxfc3BlYyhQb3MsIGNvbG9yID0gIndoaXRlIiwgYWxpZ24gPSAiYyIsIA0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gZmFjdG9yKFBvcywgYygiQyIsICJQRiIsICJTRiIsICJTRyIsICJQRyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3NDb2xvckNvZGUpKSkgJT4lDQogICAga2FibGUoZXNjYXBlID0gRkFMU0UsIGNhcHRpb24gPSAiQWxsLVRpbWUgRmllbGQgR29hbHMgJSBMZWFkZXJzIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoMiwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDExLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoqVG8gcXVhbGlmeSBmb3IgbGVhZ3VlIGxlYWRlcnMsIGEgcGxheWVyIG11c3QgbWVldCB0aGUgW1JhdGUgU3RhdGlzdGljIFJlcXVpcmVtZW50c10oaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2Fib3V0L3JhdGVfc3RhdF9yZXEuaHRtbCkqDQoNCjwvZGl2Pg0KDQo8YnIvPg0KDQpHZW5lcmF0aW5nIHBsb3QuLi4NCg0KYGBge3IgZmlnLndpZHRoID0gOX0NCkxhYmVsRml4IDwtIEFsbFRpbWVGRy4gJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgbXV0YXRlKFBsYXllciA9IGZjdF9yZW9yZGVyKFBsYXllciwgZGVzYyhGRy4pLCBmdW49bWVhbiksDQogICAgICAgICAgIEZHYSA9IEZHYS1GR20sDQogICAgICAgICAgIEZHLiA9IHBhc3RlKEZHLiAqIDEwMCwgIiUiKSkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgQ291bnQsIEZHbTpGR2EsIC1jKFBsYXllciwgRkcuKSkgJT4lDQogICAgZmlsdGVyKFBhcmFtZXRlciA9PSAiRkdhIikNCiAgICANCkFsbFRpbWVGRy4gJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgbXV0YXRlKFBsYXllciA9IGZjdF9yZW9yZGVyKFBsYXllciwgZGVzYyhGRy4pLCBmdW49bWVhbiksDQogICAgICAgICAgIEZHYSA9IEZHYS1GR20pICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCBGR206RkdhLCAtYyhQbGF5ZXIsIEZHLikpICU+JQ0KICAgIGdncGxvdChhZXMoUGxheWVyLCBDb3VudCwgZmlsbCA9IFBhcmFtZXRlcikpICsgDQogICAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ3N0YWNrJykgKw0KICAgIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gRkcuLA0KICAgICAgICAgICAgICAgICAgIHkgPSAyMTAwMCksDQogICAgICAgICAgICAgICBkYXRhID0gTGFiZWxGaXgsDQogICAgICAgICAgICAgICBzdGF0ID0gJ2lkZW50aXR5JywNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gInN0YWNrIiwNCiAgICAgICAgICAgICAgIGZpbGwgPSAiI0ZGNDAwMCIsDQogICAgICAgICAgICAgICBjb2xvciA9ICIjMDBGRkZGIiwNCiAgICAgICAgICAgICAgIHNpemUgPSA0LjUpICsNCiAgICBnZ3RpdGxlKCJBbGwtVGltZSBGaWVsZCBHb2FscyBQZXJjZW50YWdlIExlYWRlcnMiKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICAgIHhsYWIoIiIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHM9YygiRkctbWlzc2VkICAiLCAiRkctbWFkZSIpLCB2YWx1ZXM9YygiIzdGN0ZGRiIsICIjMDAwMEZGIikpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KYGBgDQoNCjxici8+DQoNClNpbmNlIHRoZSBsZWFkZXJib2FyZCBpcyB0b3RhbGx5IG9jY3VwaWVkIGJ5IHBvc3QgcGxheWVycywgbmF0dXJhbGx5IEknZCBsaWtlIHRvIHNlZSB0aGUgYmVzdCBGRyUgaW4gZWFjaCBwb3NpdGlvbiwgc28gaGVyZSBpdCBpcy4NCg0KYGBge3J9DQpUb3BGRy5Qb3MgPC0gQWxsVGltZUZHLiAlPiUNCiAgICBncm91cF9ieShQb3MpICU+JQ0KICAgIHN1bW1hcmlzZShQbGF5ZXIgPSBQbGF5ZXJbd2hpY2gubWF4KEZHLildLA0KICAgICAgICAgICAgICBUZWFtID0gVGVhbVt3aGljaC5tYXgoRkcuKV0sDQogICAgICAgICAgICAgIEFjdGl2ZVllYXJzID0gQWN0aXZlWWVhcnNbd2hpY2gubWF4KEZHLildLA0KICAgICAgICAgICAgICBHYW1lcyA9IEdhbWVzW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgTVBHID0gTVBHW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgUFBHID0gUFBHW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkdtID0gRkdtW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkdhID0gRkdhW3doaWNoLm1heChGRy4pXSwNCiAgICAgICAgICAgICAgRkcuID0gRkcuW3doaWNoLm1heChGRy4pXSkNCmBgYA0KDQo8ZGl2IGlkPSJUb3BCeVBvcyIgY2xhc3M9ImNsZWFyIj4NCjxoMT5CZXN0IEZHJSBieSBQb3NpdGlvbjwvaDE+ICANCjxkaXYgY2xhc3M9IlRvcFBsYXllciIgaWQ9IlRvcEFscGhhIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICNGRjAwMDA7dGV4dC1hbGlnbjogYzsiID5DPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cHM6Ly9hay1zdGF0aWMuY21zLm5iYS5jb20vd3AtY29udGVudC91cGxvYWRzL2hlYWRzaG90cy9uYmEvbGF0ZXN0LzI2MHgxOTAvMjAxNTk5LnBuZyIgYWx0PSIiPjwvaW1nPjwvc3Bhbj48L2gzPg0KPGRpdiBjbGFzcz0iUE5hbWUiPjxoND48YnIvPmByIFRvcEZHLlBvcyRQbGF5ZXJbMV1gPC9oND48L2Rpdj4NCjxkaXYgY2xhc3M9IlBjdCIgaHJlZj0iIj5gciBUb3BGRy5Qb3MkRkcuWzFdYDwvZGl2PiAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGRy5Qb3MkRkdtWzFdLCAiLyIsIFRvcEZHLlBvcyRGR2FbMV0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRkcuUG9zJFBQR1sxXWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRkcuUG9zJEFjdGl2ZVllYXJzWzFdYDwvbGk+CQ0KPC91bD4NCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiPg0KPGgzPjxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0ZGQTUwMDt0ZXh0LWFsaWduOiBjOyIgPlBGPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cDovL2FrLXN0YXRpYy5jbXMubmJhLmNvbS93cC1jb250ZW50L3VwbG9hZHMvaGVhZHNob3RzL25iYS9sYXRlc3QvMjYweDE5MC8xMDExNjEucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wRkcuUG9zJFBsYXllclsyXWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcEZHLlBvcyRGRy5bMl1gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGRy5Qb3MkRkdtWzJdLCAiLyIsIFRvcEZHLlBvcyRGR2FbMl0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRkcuUG9zJFBQR1syXWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRkcuUG9zJEFjdGl2ZVllYXJzWzJdYDwvbGk+CQkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiPg0KPGgzPjxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0REREQwMDt0ZXh0LWFsaWduOiBjOyIgPlNGPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cHM6Ly9pbWFnZS5pYmIuY28vZ3ZrVzVBL1BsYXllci1DZWRyaWMtTWF4d2VsbC5wbmciIGFsdD0iIj48L2ltZz48L3NwYW4+PC9oMz4NCjxkaXYgY2xhc3M9IlBOYW1lIj48aDQ+PGJyLz5gciBUb3BGRy5Qb3MkUGxheWVyWzNdYDwvaDQ+PC9kaXY+DQo8ZGl2IGNsYXNzPSJQY3QiIGhyZWY9IiI+YHIgVG9wRkcuUG9zJEZHLlszXWA8L2Rpdj4gICAgICAgICANCjx1bD4NCjxsaT48Yj5GRzogPC9iPmByIHBhc3RlKFRvcEZHLlBvcyRGR21bM10sICIvIiwgVG9wRkcuUG9zJEZHYVszXSlgPC9saT4NCjxsaT48Yj5QUEc6IDwvYj5gciBUb3BGRy5Qb3MkUFBHWzNdYDwvbGk+DQo8bGk+PGI+WWVhcnM6IDwvYj5gciBUb3BGRy5Qb3MkQWN0aXZlWWVhcnNbM11gPC9saT4JCQ0KPC91bD4gDQo8L2Rpdj4NCg0KPGRpdiBjbGFzcz0iVG9wUGxheWVyIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICMwMDAwRkY7dGV4dC1hbGlnbjogYzsiID5TRzwvc3Bhbj48YnIvPjxzcGFuPjxpbWcgY2xhc3M9InBob3RvIiBzcmM9Imh0dHBzOi8vaW1hZ2UuaWJiLmNvL2NucVQycS9QbGF5ZXItTGV3aXMtTGxveWQucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wRkcuUG9zJFBsYXllcls0XWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcEZHLlBvcyRGRy5bNF1gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGRy5Qb3MkRkdtWzRdLCAiLyIsIFRvcEZHLlBvcyRGR2FbNF0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRkcuUG9zJFBQR1s0XWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRkcuUG9zJEFjdGl2ZVllYXJzWzRdYDwvbGk+CQkNCjwvdWw+IA0KPC9kaXY+DQoNCjxkaXYgY2xhc3M9IlRvcFBsYXllciI+DQo8aDM+PHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjMzJDRDMyO3RleHQtYWxpZ246IGM7IiA+UEc8L3NwYW4+PGJyLz48c3Bhbj48aW1nIGNsYXNzPSJwaG90byIgc3JjPSJodHRwczovL2ltYWdlLmliYi5jby9lMk1VOFYvUGxheWVyLU1hdXJpY2UtQ2hlZWtzLnBuZyIgYWx0PSIiPjwvaW1nPjwvc3Bhbj48L2gzPg0KPGRpdiBjbGFzcz0iUE5hbWUiPjxoND48YnIvPmByIFRvcEZHLlBvcyRQbGF5ZXJbNV1gPC9oND48L2Rpdj4NCjxkaXYgY2xhc3M9IlBjdCIgaHJlZj0iIj5gciBUb3BGRy5Qb3MkRkcuWzVdYDwvZGl2PiAgICAgICAgIA0KPHVsPg0KPGxpPjxiPkZHOiA8L2I+YHIgcGFzdGUoVG9wRkcuUG9zJEZHbVs1XSwgIi8iLCBUb3BGRy5Qb3MkRkdhWzVdKWA8L2xpPg0KPGxpPjxiPlBQRzogPC9iPmByIFRvcEZHLlBvcyRQUEdbNV1gPC9saT4NCjxsaT48Yj5ZZWFyczogPC9iPmByIFRvcEZHLlBvcyRBY3RpdmVZZWFyc1s1XWA8L2xpPgkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KIyMjIEZpZWxkIEdvYWxzIHBlciAzNiBNaW51dGVzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBtYWxpcyBwb3NzaW0gZGVsZW5pdGkgcXVpIGF0LCBlb3MgYWxpYSBkZWNvcmUgc2NyaXBzZXJpdCBldCwgZXggZXN0IGRpY2FudCBkb2NlbmRpLiBOZSBoYXMgaW52ZW5pcmUgdm9sdXB0YXRpYnVzLiBQcmkgaWQgdml2ZW5kbyB2dWxwdXRhdGUgdGhlb3BocmFzdHVzISBUZSB2aW0gZGVmaW5pdGlvbmVtIHZpdHVwZXJhdG9yaWJ1cywgZ3JhZWNlIHNpbmd1bGlzIGludGVycHJldGFyaXMgbWVpIGVpPyBQcmkgZXUgbWVuYW5kcmkgc2Vuc2VyaXQ/DQoNCjxici8+DQoNCjxkaXYgY2xhc3M9InRhYmxlIj4NCg0KYGBge3J9DQpBbGxUaW1lRkdtYSA8LSBOQkEgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBmaWx0ZXIoc3VtKEcpID49IDQwMCkgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zaXRpb24pLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEFjdGl2ZVllYXJzID0gcGFzdGUoZ2V0bW9kZShZZWFyU3RhcnQpLCAiLSIsIGdldG1vZGUoWWVhckVuZCkpLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHKSwNCiAgICAgICAgICAgICAgTWludXRlcyA9IHN1bShNUCksDQogICAgICAgICAgICAgIE1QRyA9IHJvdW5kKE1pbnV0ZXMgLyBHYW1lcywgMSksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKHN1bShQVFMpL3N1bShHKSwgMSksDQogICAgICAgICAgICAgIEZHbV8zNiA9IHJvdW5kKChzdW0oRkcpIC8gTWludXRlcykgKiAzNiwgMSksDQogICAgICAgICAgICAgIEZHbV9HID0gcm91bmQoKHN1bShGRykgLyBHYW1lcyksIDEpLA0KICAgICAgICAgICAgICBGR2FfMzYgPSByb3VuZCgoc3VtKEZHQSkgLyBNaW51dGVzKSAqMzYsIDEpLA0KICAgICAgICAgICAgICBGR2FfRyA9IHJvdW5kKChzdW0oRkdBKSAvIEdhbWVzKSwgMSksDQogICAgICAgICAgICAgIEZHLiA9IHJvdW5kKHN1bShGRykvc3VtKEZHQSksIDMpKSAlPiUNCiAgICBzZWxlY3QoLSJNaW51dGVzIikNCg0KQWxsVGltZUZHbWEgJT4lDQogICAgYXJyYW5nZShkZXNjKEZHbV8zNiksIGRlc2MoRkcuKSkgJT4lDQogICAgaGVhZChuPTEwKSAlPiUNCiAgICBtdXRhdGUoUmFuayA9IG1pbl9yYW5rKGRlc2MoRkdtXzM2KSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBjYXB0aW9uID0gIkZpZWxkIEdvYWxzIE1hZGUgcGVyIDM2IE1pbnV0ZXMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IFQsIGZvbnRfc2l6ZSA9IDExKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoYygyLCAxMyksIGJvbGQgPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYyg5LCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCmBgYHtyfQ0KQWxsVGltZUZHbWEgJT4lDQogICAgYXJyYW5nZShkZXNjKEZHYV8zNiksIGRlc2MoRkcuKSkgJT4lDQogICAgaGVhZChuPTEwKSAlPiUNCiAgICBtdXRhdGUoUmFuayA9IG1pbl9yYW5rKGRlc2MoRkdhXzM2KSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBjYXB0aW9uID0gIkZpZWxkIEdvYWwgQXR0ZW1wdHMgcGVyIDM2IE1pbnV0ZXMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IFQsZm9udF9zaXplID0gMTEpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBULCBjb2xvciA9ICJ5ZWxsb3ciLCBiYWNrZ3JvdW5kID0gIiNGRjAwMDAiKSAlPiUNCiAgICBjb2x1bW5fc3BlYyhjKDIsIDEzKSwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDExLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCjwvZGl2Pg0KDQo8YnIvPg0KDQpQcm9ibyBlZmZpY2llbmRpIHNlYSBlYSwgbmUgZHVpcyBhcmd1bWVudHVtIHBlcnNlcXVlcmlzIHZlbC4gVXQgZHVvIGZlcnJpIGlwc3VtLCBkdW8gdmlkaXQgZXJyb3Igbm8sIGFkIHNlZCBkaWN0YSBzdWF2aXRhdGUgZGlzc2VudGlldC4gTmUgYXVkaWFtIHZvY2lidXMgZXN0LiBQb3NzZSBpbnZlbmlyZSB2aXggaW4uDQoNCmBgYHtyIGZpZy53aWR0aD05fQ0KQWxsVGltZUZHbWEgJT4lDQogICAgYXJyYW5nZShkZXNjKEZHbV8zNiksIGRlc2MoRkcuKSkgJT4lDQogICAgbXV0YXRlKFBsYXllciA9IHJlb3JkZXIoUGxheWVyLCBkZXNjKEZHbV8zNiksIEZVTj1tZWFuKSkgJT4lDQogICAgaGVhZChuPTEwKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKGdyb3VwID0gMSkpICsNCiAgICBnZW9tX3NlZ21lbnQoYWVzKHg9UGxheWVyLCB4ZW5kPVBsYXllciwgeT1GR21fMzYsIHllbmQ9RkdhXzM2KSwgY29sb3I9ImJsYWNrIikgKw0KICAgIGdlb21fcG9pbnQoYWVzKFBsYXllciwgRkdtXzM2LCBjb2xvcj0iI0ZGNTgwMCIpLCBzaXplPTUpICsNCiAgICBnZW9tX3BvaW50KGFlcyhQbGF5ZXIsIEZHYV8zNiwgY29sb3I9IiNGRjU4MDAiKSwgc2l6ZT0yLjUpICsNCiAgICBnZW9tX3BvaW50KGFlcyhQbGF5ZXIsIEZHbV9HLCBjb2xvcj0iIzAwOWRmZiIpLCBzaXplPTUsIHNoYXBlPTE4KSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICAgIGdndGl0bGUoIkZHIE1hZGUgcGVyIDM2IE1pbnV0ZXMgdnMuIEZHIE1hZGUgcGVyIEdhbWUiKSArDQogICAgeGxhYigiIikgKw0KICAgIHlsYWIoIkZHIE1hZGUiKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsIHZhbHVlcz1jKCIjMDA5ZGZmIiwgIiNGRjU4MDAiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkZHYS9tIHBlciBHYW1lIiwgIkZHYS9tIHBlciAzNiBtaW51dGVzIiksDQogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHJldmVyc2U9VFJVRSkpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKQ0KYGBgDQoNCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KDQojIyMgQklHIG5hbWVzIGluIEZpZWxkIEdvYWxzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCkZvciBzaG9vdGluZywgaW4gZ2VuZXJhbCwgSSdkIGxpa2UgdG8gYXBwbHkgc2ltcGxlciBhcHByb2FjaCwgZW1waGFzaXppbmcgcGVyY2VudGFnZSBvdmVyIG1hZGUgYnkgZG91YmxpbmcgaXQuIEZpZWxkIEdvYWxzIHdpbGwgYmUgc29ydGVkIGFjY29yZGluZyB0byB0aGUgZm9sbG93aW5nIGFyYnJpdGFyeSBjcml0ZXJpYSA6DQoNCiogQW5udWFsIGxlYWd1ZSBsZWFkZXIgaW4gRmllbGQgR29hbHMgcGVyY2VudGFnZSBjYXRlZ29yeSBlYXJuIDIgcG9pbnQuDQoqIFRvcCAxMCBhbGwtdGltZSBsZWFkZXJzIGluIEZpZWxkIEdvYWxzIHBlcmNlbnRhZ2UgY2F0ZWdvcnkgZWFybiAyIHBvaW50cy4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gRmllbGQgR29hbHMgbWFkZSBjYXRlZ29yeSB3aXRoIEZHJSBtb3JlIHRoYW4gNTUlIGVhcm4gMSBwb2ludC4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gRmllbGQgR29hbHMgbWFkZSBwZXIgMzYgbWludXRlcyBjYXRlZ29yeSB3aXRoIEZHJSBtb3JlIHRoYW4gNTUlIGVhcm4gMSBwb2ludC4NCg0KQ2FsY3VsYXRlIHRoZSBwb2ludHMgYW5kIHdlIGhhdmUgdGhlIHJhbmshDQoNCmBgYHtyIGZpZy53aWR0aCA9IDguOH0NCiMgRmlsdGVyIHRvdGFsIEZpZWxkIEdvYWxzIG1hZGUgd2l0aCBGRyUgPiAuNTUNCkFsbFRpbWVGR20gPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKEZHbSA9IHN1bShGRyksDQogICAgICAgICAgICAgIEZHYSA9IHN1bShGR0EpLA0KICAgICAgICAgICAgICBGRy4gPSBGR20gLyBGR2EpICU+JQ0KICAgIGZpbHRlcihGRy4gPiAwLjU1KSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoRkdtKSkgJT4lIGhlYWQoMTApDQoNCiMgRmlsdGVyIEZpZWxkIEdvYWxzIG1hZGUgcGVyIDM2IG1pbnV0ZXMgd2l0aCBGRyUgPiAuNTUNCkFsbFRpbWVGR20zNiA8LSBBbGxUaW1lRkdtYSAlPiUNCiAgICBmaWx0ZXIoRkcuID49IDAuNTUpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhGR21fMzYpKSAlPiUNCiAgICBoZWFkKG49MTApDQoNCk5hbWVzRkcgPC0gZGF0YS5mcmFtZShOYW1lID0gYyhhcy5jaGFyYWN0ZXIocmVwKFRvcEZHLiRQbGF5ZXIsIDIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocmVwKGhlYWQoQWxsVGltZUZHLiRQbGF5ZXIsIDEwKSwgMikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihBbGxUaW1lRkdtJFBsYXllciksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKEFsbFRpbWVGR20zNiRQbGF5ZXIpKSkNCk5hbWVzRkcgJT4lIGNvdW50KE5hbWUpICU+JSBhcnJhbmdlKGRlc2MobikpICU+JSANCiAgICB3b3JkY2xvdWQyKHNpemU9MC40LCBjb2xvcj0ncmFuZG9tLWxpZ2h0JywgYmFja2dyb3VuZENvbG9yPSJibGFjayIpDQpgYGANCg0KPGJyLz4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQotLS0NCg0KPGJyLz4NCjxici8+DQoNCiMjIEZyZWUgVGhyb3cgKEZUKQ0KDQpJbiB0aGUgdHdvIGNhdGVnb3JpZXMgYWJvdmUgKEZHIGFuZCAzUCksIGEgcGxheWVyIGNhbiBkZWxpYmVyYXRlbHkgY2hvb3NlIHRvIG1ha2UgYSBzaG90IGF0dGVtcHQuIEhvd2V2ZXIsIGluIEZUIHRoZXkgbmVlZCB0byBiZSBmb3VsZWQgYnkgb3Bwb25lbnQgcGxheWVyIHRvIGF0dGVtcHQgdGhlIHNob3QuDQoNClF1aXRlIG9mdGVuLCBhIHBsYXllciB3aXRoIGdvb2QgRkclIGJ1dCBiYWQgRlQlLCB3aGljaCBpcyBjb21tb24gZm9yIGEgYmlnIGd1eSAoQ2VudGVycyBvciBQb3dlciBGb3J3YXJkKSwgd2lsbCBnZXQgZm91bGVkIGludGVudGlvbmFsbHkgYnkgdGhlIG9wcG9uZW50LiBTaGFxdWlsbGUgTydOZWFsIChDKSwgd2l0aCBoaXMgNTguMiUgRkcgYW5kIDUyLjclIEZULCBpcyB0aGUgbW9zdCBub3RhYmxlIGV4YW1wbGUgZm9yIHRoaXMsIGhlbmNlIHRoZSB0ZXJtIFtIYWNrLWEtU2hhcV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSGFjay1hLVNoYXEpLg0KDQpOb3cgbGV0J3MgZ2V0IHN0YXJ0ZWQuLg0KDQo8YnIvPg0KDQojIyMgQW5udWFsIEZyZWUgVGhyb3dzICUgTGVhZ3VlIExlYWRlcnMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KTWF1cmlzIGlhY3VsaXMgbGlndWxhIGVsZWlmZW5kIGRvbG9yIHNlbXBlciwgaWQgZGlnbmlzc2ltIGR1aSBkaWduaXNzaW0uIFBlbGxlbnRlc3F1ZSBuZXF1ZSBsaWd1bGEsIHNhZ2l0dGlzIHF1aXMgZW5pbSB2aXRhZSwgcHJldGl1bSBzY2VsZXJpc3F1ZSBtYWduYS4gUXVpc3F1ZSBpbiBwb3N1ZXJlIGVsaXQsIHF1aXMgbHVjdHVzIG1hdXJpcy4gUHJvaW4gZWdldCBhdWN0b3IgbGVjdHVzLiBDcmFzIHRpbmNpZHVudCBsYW9yZWV0IGVsaXQsIGFjIHVsdHJpY2llcyBqdXN0byBwdWx2aW5hciBlZ2V0LiBBbGlxdWFtIHByZXRpdW0gZmFjaWxpc2lzIGF1Z3VlIGluIHNjZWxlcmlzcXVlLiBOdW5jIGx1Y3R1cyB2ZW5lbmF0aXMgc29sbGljaXR1ZGluLiBWaXZhbXVzIGZhY2lsaXNpcyBub24gbWFnbmEgbmVjIGltcGVyZGlldC4gQ3VyYWJpdHVyIGV1IGxpYmVybyBhdCBtYWduYSB0aW5jaWR1bnQgYXVjdG9yLiBRdWlzcXVlIGxhb3JlZXQgZGFwaWJ1cyBzb2xsaWNpdHVkaW4uDQoNCmBgYHtyfQ0KVG9wRlQuIDwtIE5CQSAlPiUNCiAgICBmaWx0ZXIoRlQuID4gMCkgJT4lDQogICAgZ3JvdXBfYnkoWWVhciwgUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoUG9zID0gZ2V0bW9kZShQb3MpLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBNUEcgPSByb3VuZChzdW0oTVApL0dhbWVzLCAxKSwNCiAgICAgICAgICAgICAgUFBHID0gcm91bmQoc3VtKFBUUykvR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBGVG0gPSBzdW0oRlQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBKSwNCiAgICAgICAgICAgICAgRlQuID0gcm91bmQoRlRtL0ZUYSwgMykpICU+JQ0KICAgIGZpbHRlcihpZmVsc2UoWWVhciAlaW4lIDE5NTA6MTk1MSwgRlRtID49IDE3MCwNCiAgICAgICAgICAgICBpZmVsc2UoWWVhciAlaW4lIDE5NTI6MTk1NSwgRlRtID49IDE4MCwNCiAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk1NjoxOTU5LCBGVG0gPj0gMTkwLA0KICAgICAgICAgICAgICAgICBpZmVsc2UoWWVhciAlaW4lIDE5NjAsIEZUbSA+PSAxODUsDQogICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTYxOjE5NjIsIEZUbSA+PSAyMDAsDQogICAgICAgICAgICAgICAgICAgICBpZmVsc2UoWWVhciAlaW4lIDE5NjM6MTk2NiwgRlRtID49IDIxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTY3OjE5NjgsIEZUbSA+PSAyMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTY5LCBGVG0gPj0gMjMwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTcwLCBHYW1lcyA+PSA3MCAmIEZUYSA+PSAzNTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk3MToxOTcyLCBGVGEgPj0gMzUwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMTk3MzoxOTc0LCBGVGEgPj0gMTYwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFllYXIgJWluJSAxOTk5LCBGVG0gPj0gNzYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShZZWFyICVpbiUgMjAxMiwgRlRtID49IDEwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGVG0gPj0gMTI1KSkpKSkpKSkpKSkpKSkgJT4lDQogICAgZ3JvdXBfYnkoWWVhcikgJT4lDQogICAgc3VtbWFyaXNlKFBsYXllciA9IFBsYXllclt3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIFRlYW0gPSBUZWFtW3doaWNoLm1heChGVC4pXSwNCiAgICAgICAgICAgICAgUG9zID0gUG9zW3doaWNoLm1heChGVC4pXSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBHYW1lc1t3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIE1QRyA9IE1QR1t3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIFBQRyA9IFBQR1t3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIEZUbSA9IEZUbVt3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIEZUYSA9IEZUYVt3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIEZULiA9IG1heChGVC4sIG5hLnJtID0gVCkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhZZWFyKSkNCg0KVG9wRlQuICU+JQ0KICAgIG11dGF0ZShQb3MgPSBjZWxsX3NwZWMoUG9zLCBjb2xvciA9ICJ3aGl0ZSIsIGFsaWduID0gImMiLCANCiAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IGZhY3RvcihQb3MsIGMoIkMiLCAiUEYiLCAiU0YiLCAiU0ciLCAiUEciKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zQ29sb3JDb2RlKSkpICU+JQ0KICAgIGthYmxlKGVzY2FwZSA9IEZBTFNFLCBjYXB0aW9uID0gIkFubnVhbCBGcmVlIFRocm93cyAlIExlYWd1ZSBMZWFkZXJzIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoMiwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEwLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCipUbyBxdWFsaWZ5IGZvciBsZWFndWUgbGVhZGVycywgYSBwbGF5ZXIgbXVzdCBtZWV0IHRoZSBbUmF0ZSBTdGF0aXN0aWMgUmVxdWlyZW1lbnRzXShodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vYWJvdXQvcmF0ZV9zdGF0X3JlcS5odG1sKSoNCg0KPGJyLz4NCg0KR2VuZXJhdGluZyBwbG90Li4uDQoNCmBgYHtyIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gOX0NCm1lYW5GVC5ZZWFyIDwtIE5CQSAlPiUNCiAgICBncm91cF9ieShZZWFyKSAlPiUNCiAgICBmaWx0ZXIoRlQuID4gMCkgJT4lDQogICAgc3VtbWFyaXNlKEZULiA9IHN1bShGVCkvc3VtKEZUQSkpDQoNClRGVC4gPC0gVG9wRlQuICU+JQ0KICAgIGdncGxvdCgpICsNCiAgICBnZW9tX2JhcihhZXMoWWVhciwgRlQuLCBmaWxsID0gUG9zLCB0ZXh0ID0gcGFzdGUoIlBsYXllcjoiLCBQbGF5ZXIpKSwgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgICBnZW9tX2xpbmUoZGF0YT1tZWFuRlQuWWVhciwgYWVzKFllYXIsIEZULiwgbGluZXR5cGUgPSAiQXZlcmFnZSBsaW5lIiksIGNvbG9yID0gImJsYWNrIikgKw0KICAgIGdndGl0bGUoIkFubnVhbCBGcmVlIFRocm93cyBQZXJjZW50YWdlIExlYWRlcnMiKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihUb3BGVC4kRlQuKSwgY29sID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk1MCwgMjAyMCwgMTApKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwoIlBvcyIsIHZhbHVlcyA9IFBvc0NvbG9yQ29kZSkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpDQoNCnBURlQuIDwtIGdncGxvdGx5KFRGVC4pDQoNCiNhcGlfY3JlYXRlKHBURlQuLCBmaWxlbmFtZSA9ICJBbm51YWxGVCUgTGVhZGVycyIpDQpgYGANCg0KPGRpdj48YSBocmVmPSJodHRwczovL3Bsb3QubHkvfk51bk5vRGVDYWJ1bmljLzI0Lz9zaGFyZV9rZXk9RjR0TXk3SlpBSldlaXp3RDZzcmlpNCIgdGFyZ2V0PSJfYmxhbmsiIHRpdGxlPSJBbm51YWxGVCUgTGVhZGVycyIgc3R5bGU9ImRpc3BsYXk6IGJsb2NrOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48aW1nIHNyYz0iaHR0cHM6Ly9wbG90Lmx5L35OdW5Ob0RlQ2FidW5pYy8yNC5wbmc/c2hhcmVfa2V5PUY0dE15N0paQUpXZWl6d0Q2c3JpaTQiIGFsdD0iQW5udWFsRlQlIExlYWRlcnMiIHN0eWxlPSJtYXgtd2lkdGg6IDEwMCU7d2lkdGg6IDg4MHB4OyIgIHdpZHRoPSI4ODAiIG9uZXJyb3I9InRoaXMub25lcnJvcj1udWxsO3RoaXMuc3JjPSdodHRwczovL3Bsb3QubHkvNDA0LnBuZyc7IiAvPjwvYT48c2NyaXB0IGRhdGEtcGxvdGx5PSJOdW5Ob0RlQ2FidW5pYzoyNCIgc2hhcmVrZXktcGxvdGx5PSJGNHRNeTdKWkFKV2VpendENnNyaWk0IiBzcmM9Imh0dHBzOi8vcGxvdC5seS9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+PC9kaXY+PGJyLz4NCg0KPGRpdiBjbGFzcz0iRmFjdCI+DQoNCjx1bCBjbGFzcz0iQ3VzdG9tTGlzdCI+DQoNCjxsaT5BdmVyYWdlIHRvcCBGVCUgaXMgYHIgcm91bmQoc3VtKFRvcEZULiRGVG0pL3N1bShUb3BGVC4kRlRhKSwgMylgLjwvbGk+DQo8bGk+SGlnaGVzdCB0b3AgRlQlIGlzIGByIG1heChUb3BGVC4kRlQuKWAsIGluIHNlYXNvbiBgciBUb3BGVC4kWWVhclt3aGljaC5tYXgoVG9wRlQuJEZULildYC48L2xpPg0KPGxpPkxvd2VzdCB0b3AgRlQlIGlzIGByIG1pbihUb3BGVC4kRlQuKWAsIGluIHNlYXNvbiBgciBUb3BGVC4kWWVhclt3aGljaC5taW4oVG9wRlQuJEZULildYC48L2xpPg0KPGxpPlRoZSA8c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICMzMkNEMzI7dGV4dC1hbGlnbjogYzsiID5QRzwvc3Bhbj4gZG9taW5hdGVkIHRoZSBsZWFndWUgYnkgYHIgcm91bmQobnJvdyhUb3BGVC5bVG9wRlQuJFBvcyA9PSAiUEciLF0pL25yb3coVG9wRlQuKSoxMDAsIDEpYCUsIDxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogIzAwMDBGRjt0ZXh0LWFsaWduOiBjOyIgPlNHPC9zcGFuPiB3aXRoIGByIHJvdW5kKG5yb3coVG9wRlQuW1RvcEZULiRQb3MgPT0gIlNHIixdKS9ucm93KFRvcEZULikqMTAwLCAxKWAlLCBmb2xsb3dlZCBieSAsIHRoZW4gPHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjRERERDAwO3RleHQtYWxpZ246IGM7IiA+U0Y8L3NwYW4+PGJyLz48c3Bhbj4gYWRkZWQgd2l0aCBgciByb3VuZChucm93KFRvcEZULltUb3BGVC4kUG9zID09ICJTRiIsXSkvbnJvdyhUb3BGVC4pKjEwMCwgMSlgJSwgYW5kIGZpbmFsbHkgPHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjRkZBNTAwO3RleHQtYWxpZ246IGM7IiA+UEY8L3NwYW4+IGNvbXBsZXRlZCB0aGUgbGVhZGVyYm9hcmQgd2l0aCBvbmx5IGByIHJvdW5kKG5yb3coVG9wRlQuW1RvcEZULiRQb3MgPT0gIlBGIixdKS9ucm93KFRvcEZULikqMTAwLCAxKWAlLiBBbmQgdGhlcmUncyBvbmx5IDEgY2VudGVyIGV2ZXIgaW4gdGhlIGFubnVhbCBGVCUgbGVhZGVyYm9hcmQsIEphY2sgU2lrbWEgd2l0aCAwLjkyMiBGVC48L2xpPg0KDQo8L3VsPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCg0KPC9kaXY+DQoNCjxici8+DQo8YnIvPg0KDQojIyMgQWxsLVRpbWUgRnJlZSBUaHJvd3MgJSBMZWFkZXJzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNCkFlbmVhbiBpZCBjb25zZXF1YXQgZXJvcy4gVml2YW11cyB2aXRhZSBjdXJzdXMgbnVsbGEsIG5vbiB2dWxwdXRhdGUgbGFjdXMuIFZlc3RpYnVsdW0gdGVtcG9yIG9ybmFyZSBkdWkgYSBmZXJtZW50dW0uIEZ1c2NlIGFsaXF1ZXQgbGVvIGluIGp1c3RvIGVsZWlmZW5kIHJob25jdXMuIExvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQuIFByb2luIG1vbGxpcyBsZW8gYSBvZGlvIGFsaXF1ZXQgY29uc2VxdWF0LiBEb25lYyBtb2xlc3RpZSBmcmluZ2lsbGEgY29udmFsbGlzLiBVdCB1dCB1bGxhbWNvcnBlciBxdWFtLCBub24gc2FnaXR0aXMgdHVycGlzLiBQcmFlc2VudCBmZXJtZW50dW0gbWF4aW11cyB0b3J0b3IsIGlkIG1vbGVzdGllIHNlbSBmcmluZ2lsbGEgZWdlc3Rhcy4gRXRpYW0gaW4gZGFwaWJ1cyBxdWFtLCBpZCBwb3J0dGl0b3IgbG9yZW0uDQoNCmBgYHtyfQ0KQWxsVGltZUZULiA8LSBOQkEgJT4lDQogICAgZmlsdGVyKEZULiA+IDApICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBBY3RpdmVZZWFycyA9IHBhc3RlKGdldG1vZGUoWWVhclN0YXJ0KSwgIi0iLCBnZXRtb2RlKFllYXJFbmQpKSwNCiAgICAgICAgICAgICAgR2FtZXMgPSBzdW0oRyksDQogICAgICAgICAgICAgIE1QRyA9IHJvdW5kKHN1bShNUCkgLyBHYW1lcywgMSksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKHN1bShQVFMpIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBGVG0gPSBzdW0oRlQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBKSwNCiAgICAgICAgICAgICAgRlQuID0gcm91bmQoRlRtL0ZUYSwgMykpICU+JQ0KICAgIGZpbHRlcihGVG0gPj0gMTIwMCkgJT4lDQogICAgYXJyYW5nZShkZXNjKEZULiksIGRlc2MoRlRtKSkgJT4lDQogICAgbXV0YXRlKFJhbmsgPSBtaW5fcmFuayhkZXNjKEZULikpKSAlPiUNCiAgICBzZWxlY3QoUmFuaywgZXZlcnl0aGluZygpKQ0KDQpBbGxUaW1lRlQuICU+JQ0KICAgIGhlYWQobj0xMCkgJT4lDQogICAgbXV0YXRlKFBvcyA9IGNlbGxfc3BlYyhQb3MsIGNvbG9yID0gIndoaXRlIiwgYWxpZ24gPSAiYyIsIA0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gZmFjdG9yKFBvcywgYygiQyIsICJQRiIsICJTRiIsICJTRyIsICJQRyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3NDb2xvckNvZGUpKSkgJT4lDQogICAga2FibGUoZXNjYXBlID0gRkFMU0UsIGNhcHRpb24gPSAiQWxsLVRpbWUgRnJlZSBUaHJvd3MgJSBMZWFkZXJzIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoMiwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDExLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCjxici8+DQoNClRoZSByYW5rIHRhYmxlIHNob3dzIHRoZXJlIGFyZSB0d28gcGxheWVycyBzaGFyaW5nIHRoZSBmaXJzdCBzcG90LiBCdXQgYWN0dWFsbHksIGlmIHRoZSBudW1iZXIgbm90IHJvdW5kZWQsIFN0ZXZlIE5hc2ggaGFzIGByIEFsbFRpbWVGVC4kRlRtW0FsbFRpbWVGVC4kUGxheWVyID09ICJTdGV2ZSBOYXNoIl0vQWxsVGltZUZULiRGVGFbQWxsVGltZUZULiRQbGF5ZXIgPT0gIlN0ZXZlIE5hc2giXWAgRlQsIHdoaWxlIE1hcmsgUHJpY2UgaGFzIGByIEFsbFRpbWVGVC4kRlRtW0FsbFRpbWVGVC4kUGxheWVyID09ICJNYXJrIFByaWNlIl0vQWxsVGltZUZULiRGVGFbQWxsVGltZUZULiRQbGF5ZXIgPT0gIk1hcmsgUHJpY2UiXWAgRlQuIFNvIFN0ZXZlIE5hc2ggd29uIHRoZSB0b3Agc3BvdCENCg0KYGBge3IgZmlnLndpZHRoID0gOX0NCkxhYmVsRml4IDwtIEFsbFRpbWVGVC4gJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgbXV0YXRlKFBsYXllciA9IGZjdF9yZW9yZGVyKFBsYXllciwgZGVzYyhGVC4pLCBmdW49bWVhbiksDQogICAgICAgICAgIEZUYSA9IEZUYS1GVG0sDQogICAgICAgICAgIEZULiA9IHBhc3RlKEZULiAqIDEwMCwgIiUiKSkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgQ291bnQsIEZUbTpGVGEsIC1jKFBsYXllciwgRlQuKSkgJT4lDQogICAgZmlsdGVyKFBhcmFtZXRlciA9PSAiRlRhIikNCg0KQWxsVGltZUZULiAlPiUNCiAgICBoZWFkKDEwKSAlPiUNCiAgICBtdXRhdGUoUGxheWVyID0gZmN0X3Jlb3JkZXIoUGxheWVyLCBkZXNjKEZULiksIGZ1bj1tZWFuKSwNCiAgICAgICAgICAgRlRhID0gRlRhLUZUbSkgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgQ291bnQsIEZUbTpGVGEsIC1jKFBsYXllciwgRlQuKSkgJT4lDQogICAgZ2dwbG90KGFlcyhQbGF5ZXIsIENvdW50LCBmaWxsID0gUGFyYW1ldGVyKSkgKyANCiAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnc3RhY2snKSArDQogICAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBGVC4sDQogICAgICAgICAgICAgICAgICAgeSA9IDc1MDApLA0KICAgICAgICAgICAgICAgZGF0YSA9IExhYmVsRml4LA0KICAgICAgICAgICAgICAgc3RhdCA9ICdpZGVudGl0eScsDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJzdGFjayIsDQogICAgICAgICAgICAgICBmaWxsID0gIiNGRjQwMDAiLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiIzAwRkZGRiIsDQogICAgICAgICAgICAgICBzaXplID0gNC41KSArDQogICAgZ2d0aXRsZSgiQWxsLVRpbWUgRnJlZSBUaHJvd3MgUGVyY2VudGFnZSBMZWFkZXJzIikgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsNCiAgICB4bGFiKCIiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzPWMoIkZULW1pc3NlZCAgIiwgIkZULW1hZGUiKSwgdmFsdWVzPWMoIiNGRjRDNEMiLCIjRkYwMDAwIikpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQ0KYGBgDQoNCjxici8+DQpBbmQgaGVyZSB0aGV5IGFyZS4uLg0KDQpgYGB7cn0NClRvcEZULlBvcyA8LSBBbGxUaW1lRlQuICU+JQ0KICAgIGdyb3VwX2J5KFBvcykgJT4lDQogICAgc3VtbWFyaXNlKFBsYXllciA9IFBsYXllclt3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIFRlYW0gPSBUZWFtW3doaWNoLm1heChGVC4pXSwNCiAgICAgICAgICAgICAgQWN0aXZlWWVhcnMgPSBBY3RpdmVZZWFyc1t3aGljaC5tYXgoRlQuKV0sDQogICAgICAgICAgICAgIEdhbWVzID0gR2FtZXNbd2hpY2gubWF4KEZULildLA0KICAgICAgICAgICAgICBNUEcgPSBNUEdbd2hpY2gubWF4KEZULildLA0KICAgICAgICAgICAgICBQUEcgPSBQUEdbd2hpY2gubWF4KEZULildLA0KICAgICAgICAgICAgICBGVG0gPSBGVG1bd2hpY2gubWF4KEZULildLA0KICAgICAgICAgICAgICBGVGEgPSBGVGFbd2hpY2gubWF4KEZULildLA0KICAgICAgICAgICAgICBGVC4gPSBGVC5bd2hpY2gubWF4KEZULildKQ0KYGBgDQoNCjxkaXYgaWQ9IlRvcEJ5UG9zIiBjbGFzcz0iY2xlYXIiPg0KPGgxPkJlc3QgRlQlIGJ5IFBvc2l0aW9uPC9oMT4gIA0KPGRpdiBjbGFzcz0iVG9wUGxheWVyIj4NCjxoMz48c3BhbiBzdHlsZT0iIGNvbG9yOiB3aGl0ZTtib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmctcmlnaHQ6IDRweDsgcGFkZGluZy1sZWZ0OiA0cHg7IGJhY2tncm91bmQtY29sb3I6ICNGRjAwMDA7dGV4dC1hbGlnbjogYzsiID5DPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cHM6Ly9pbWFnZS5pYmIuY28vZFBiQ1RWL1BsYXllci1KYWNrLVNpa21hLnBuZyIgYWx0PSIiPjwvaW1nPjwvc3Bhbj48L2gzPg0KPGRpdiBjbGFzcz0iUE5hbWUiPjxoND48YnIvPmByIFRvcEZULlBvcyRQbGF5ZXJbMV1gPC9oND48L2Rpdj4NCjxkaXYgY2xhc3M9IlBjdCIgaHJlZj0iIj5gciBUb3BGVC5Qb3MkRlQuWzFdYDwvZGl2PiAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGVC5Qb3MkRlRtWzFdLCAiLyIsIFRvcEZULlBvcyRGVGFbMV0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRlQuUG9zJFBQR1sxXWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRlQuUG9zJEFjdGl2ZVllYXJzWzFdYDwvbGk+CQkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiPg0KPGgzPjxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogI0ZGQTUwMDt0ZXh0LWFsaWduOiBjOyIgPlBGPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cHM6Ly9hay1zdGF0aWMuY21zLm5iYS5jb20vd3AtY29udGVudC91cGxvYWRzL2hlYWRzaG90cy9uYmEvMTYxMDYxMjc0Mi8yMDE4LzI2MHgxOTAvMTcxNy5wbmciIGFsdD0iIj48L2ltZz48L3NwYW4+PC9oMz4NCjxkaXYgY2xhc3M9IlBOYW1lIj48aDQ+PGJyLz5gciBUb3BGVC5Qb3MkUGxheWVyWzJdYDwvaDQ+PC9kaXY+DQo8ZGl2IGNsYXNzPSJQY3QiIGhyZWY9IiI+YHIgVG9wRlQuUG9zJEZULlsyXWA8L2Rpdj4gICAgICAgICANCjx1bD4NCjxsaT48Yj5GRzogPC9iPmByIHBhc3RlKFRvcEZULlBvcyRGVG1bMl0sICIvIiwgVG9wRlQuUG9zJEZUYVsyXSlgPC9saT4NCjxsaT48Yj5QUEc6IDwvYj5gciBUb3BGVC5Qb3MkUFBHWzJdYDwvbGk+DQo8bGk+PGI+WWVhcnM6IDwvYj5gciBUb3BGVC5Qb3MkQWN0aXZlWWVhcnNbMl1gPC9saT4JCQkNCjwvdWw+IA0KPC9kaXY+DQoNCjxkaXYgY2xhc3M9IlRvcFBsYXllciI+DQo8aDM+PHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjRERERDAwO3RleHQtYWxpZ246IGM7IiA+U0Y8L3NwYW4+PGJyLz48c3Bhbj48aW1nIGNsYXNzPSJwaG90byIgc3JjPSJodHRwczovL2FrLXN0YXRpYy5jbXMubmJhLmNvbS93cC1jb250ZW50L3VwbG9hZHMvaGVhZHNob3RzL25iYS9sYXRlc3QvMjYweDE5MC82MDAwMTMucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wRlQuUG9zJFBsYXllclszXWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcEZULlBvcyRGVC5bM11gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGVC5Qb3MkRlRtWzNdLCAiLyIsIFRvcEZULlBvcyRGVGFbM10pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRlQuUG9zJFBQR1szXWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRlQuUG9zJEFjdGl2ZVllYXJzWzNdYDwvbGk+CQkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiPg0KPGgzPjxzcGFuIHN0eWxlPSIgY29sb3I6IHdoaXRlO2JvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZy1yaWdodDogNHB4OyBwYWRkaW5nLWxlZnQ6IDRweDsgYmFja2dyb3VuZC1jb2xvcjogIzAwMDBGRjt0ZXh0LWFsaWduOiBjOyIgPlNHPC9zcGFuPjxici8+PHNwYW4+PGltZyBjbGFzcz0icGhvdG8iIHNyYz0iaHR0cHM6Ly9pbWFnZS5pYmIuY28vaHF6VmhxL1BsYXllci1SYXktQWxsZW4ucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wRlQuUG9zJFBsYXllcls0XWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcEZULlBvcyRGVC5bNF1gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGVC5Qb3MkRlRtWzRdLCAiLyIsIFRvcEZULlBvcyRGVGFbNF0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRlQuUG9zJFBQR1s0XWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRlQuUG9zJEFjdGl2ZVllYXJzWzRdYDwvbGk+CQkJDQo8L3VsPiANCjwvZGl2Pg0KDQo8ZGl2IGNsYXNzPSJUb3BQbGF5ZXIiIGlkPSJUb3BBbHBoYSI+DQo8aDM+PHNwYW4gc3R5bGU9IiBjb2xvcjogd2hpdGU7Ym9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nLXJpZ2h0OiA0cHg7IHBhZGRpbmctbGVmdDogNHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjMzJDRDMyO3RleHQtYWxpZ246IGM7IiA+UEc8L3NwYW4+PGJyLz48c3Bhbj48aW1nIGNsYXNzPSJwaG90byIgc3JjPSJodHRwczovL2ltYWdlLmliYi5jby9odE15cGYvUGxheWVyLVN0ZXZlLU5hc2gucG5nIiBhbHQ9IiI+PC9pbWc+PC9zcGFuPjwvaDM+DQo8ZGl2IGNsYXNzPSJQTmFtZSI+PGg0Pjxici8+YHIgVG9wRlQuUG9zJFBsYXllcls1XWA8L2g0PjwvZGl2Pg0KPGRpdiBjbGFzcz0iUGN0IiBocmVmPSIiPmByIFRvcEZULlBvcyRGVC5bNV1gPC9kaXY+ICAgICAgICAgDQo8dWw+DQo8bGk+PGI+Rkc6IDwvYj5gciBwYXN0ZShUb3BGVC5Qb3MkRlRtWzVdLCAiLyIsIFRvcEZULlBvcyRGVGFbNV0pYDwvbGk+DQo8bGk+PGI+UFBHOiA8L2I+YHIgVG9wRlQuUG9zJFBQR1s1XWA8L2xpPg0KPGxpPjxiPlllYXJzOiA8L2I+YHIgVG9wRlQuUG9zJEZULls1XWA8L2xpPgkJCQ0KPC91bD4gDQo8L2Rpdj4NCg0KPC9kaXY+DQoNCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KIyMjIEZyZWUgVGhyb3dzIHBlciAzNiBNaW51dGVzIExlYWRlcnMNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KPGJyLz4NCg0KTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUGhhc2VsbHVzIGZlcm1lbnR1bSB0dXJwaXMgZXQganVzdG8gZmVybWVudHVtIHZpdmVycmEuIEFsaXF1YW0gaXBzdW0gbWksIGRpZ25pc3NpbSBxdWlzIGVyb3MgZmVybWVudHVtLCBwb3N1ZXJlIHRpbmNpZHVudCBudWxsYS4gUXVpc3F1ZSBzb2xsaWNpdHVkaW4sIG1hc3NhIHF1aXMgbGFjaW5pYSBhY2N1bXNhbiwgcXVhbSB1cm5hIGZpbmlidXMgYXVndWUsIGluIGJsYW5kaXQgcHVydXMgbmlzaSBpZCBuaXNpLiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBDcmFzIGVmZmljaXR1ciBuaXNsIGEgaW50ZXJkdW0gYWNjdW1zYW4uIFN1c3BlbmRpc3NlIGV0IG1heGltdXMgbWV0dXMsIHZlbCBhY2N1bXNhbiBhcmN1LiBWaXZhbXVzIG1vbGxpcyBxdWlzIGVyb3MgdXQgbW9sZXN0aWUuDQoNCjxkaXYgY2xhc3M9InRhYmxlIj4NCg0KYGBge3J9DQpBbGxUaW1lRlRtYSA8LSBOQkEgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBmaWx0ZXIoc3VtKEcpID49IDQwMCkgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBBY3RpdmVZZWFycyA9IHBhc3RlKGdldG1vZGUoWWVhclN0YXJ0KSwgIi0iLCBnZXRtb2RlKFllYXJFbmQpKSwNCiAgICAgICAgICAgICAgTWludXRlcyA9IHN1bShNUCksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBNUEcgPSByb3VuZChNaW51dGVzIC9zdW0oRyksIDEpLA0KICAgICAgICAgICAgICBQUEcgPSByb3VuZChzdW0oUFRTKS9zdW0oRyksIDEpLA0KICAgICAgICAgICAgICBGVG1fMzYgPSByb3VuZCgoc3VtKEZUKSAvIE1pbnV0ZXMpICogMzYsIDEpLA0KICAgICAgICAgICAgICBGVG1fRyA9IHJvdW5kKHN1bShGVCkgLyBHYW1lcywgMSksDQogICAgICAgICAgICAgIEZUYV8zNiA9IHJvdW5kKChzdW0oRlRBKSAvIE1pbnV0ZXMpICogMzYsIDEpLA0KICAgICAgICAgICAgICBGVGFfRyA9IHJvdW5kKHN1bShGVEEpIC8gR2FtZXMsIDEpLA0KICAgICAgICAgICAgICBGVC4gPSByb3VuZChzdW0oRlQpL3N1bShGVEEpLCAzKSkgJT4lDQogICAgc2VsZWN0KC0iTWludXRlcyIpDQoNCkFsbFRpbWVGVG1hICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhGVG1fMzYpLCBkZXNjKEZULikpICU+JQ0KICAgIGhlYWQobj0xMCkgJT4lDQogICAgbXV0YXRlKFJhbmsgPSBtaW5fcmFuayhkZXNjKEZUbV8zNikpKSAlPiUNCiAgICBzZWxlY3QoUmFuaywgZXZlcnl0aGluZygpKSAlPiUNCiAgICBtdXRhdGUoUG9zID0gY2VsbF9zcGVjKFBvcywgY29sb3IgPSAid2hpdGUiLCBhbGlnbiA9ICJjIiwgDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSBmYWN0b3IoUG9zLCBjKCJDIiwgIlBGIiwgIlNGIiwgIlNHIiwgIlBHIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc0NvbG9yQ29kZSkpKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJGcmVlIFRocm93IE1hZGUgcGVyIDM2IE1pbnV0ZXMiKSAlPiUNCiAgICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IFQsIGZvbnRfc2l6ZSA9IDExKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCwgY29sb3IgPSAieWVsbG93IiwgYmFja2dyb3VuZCA9ICIjRkYwMDAwIikgJT4lDQogICAgY29sdW1uX3NwZWMoYygyLCAxMyksIGJvbGQgPSBUKSAlPiUNCiAgICBjb2x1bW5fc3BlYyg5LCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxMywgYm9sZCA9IFQpICU+JQ0KICAgIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIzMDBweCIpDQpgYGANCg0KPGJyLz4NCg0KYGBge3J9DQpBbGxUaW1lRlRtYSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoRlRhXzM2KSwgZGVzYyhGVC4pKSAlPiUNCiAgICBoZWFkKG49MTApICU+JQ0KICAgIG11dGF0ZShSYW5rID0gbWluX3JhbmsoZGVzYyhGVGFfMzYpKSkgJT4lDQogICAgc2VsZWN0KFJhbmssIGV2ZXJ5dGhpbmcoKSkgJT4lDQogICAgbXV0YXRlKFBvcyA9IGNlbGxfc3BlYyhQb3MsIGNvbG9yID0gIndoaXRlIiwgYWxpZ24gPSAiYyIsIA0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kID0gZmFjdG9yKFBvcywgYygiQyIsICJQRiIsICJTRiIsICJTRyIsICJQRyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3NDb2xvckNvZGUpKSkgJT4lDQogICAga2FibGUoZXNjYXBlID0gRkFMU0UsIGNhcHRpb24gPSAiRnJlZSBUaHJvdyBBdHRlbXB0cyBwZXIgMzYgTWludXRlcyIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gVCwgZm9udF9zaXplID0gMTEpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBULCBjb2xvciA9ICJ5ZWxsb3ciLCBiYWNrZ3JvdW5kID0gIiNGRjAwMDAiKSAlPiUNCiAgICBjb2x1bW5fc3BlYyhjKDIsIDEzKSwgYm9sZCA9IFQpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDExLCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxMywgYm9sZCA9IFQpICU+JQ0KICAgIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIzMDBweCIpDQpgYGANCjwvZGl2Pg0KDQo8YnIvPg0KDQpOdWxsYSBmYWNpbGlzaXMsIHNlbSBlZ2V0IHBvcnRhIGx1Y3R1cywgbGVvIGRpYW0gbW9sZXN0aWUgZWxpdCwgbG9ib3J0aXMgdWx0cmljaWVzIGVuaW0gbnVuYyBldSB2ZWxpdC4gQ3VyYWJpdHVyIHRyaXN0aXF1ZSBzaXQgYW1ldCB1cm5hIGlkIHBvcnR0aXRvci4gRHVpcyBldSBwdWx2aW5hciBkb2xvci4gUXVpc3F1ZSBmZXJtZW50dW0gbG9ib3J0aXMgaW50ZXJkdW0uIE1vcmJpIGNvbnNlcXVhdCBpcHN1bSB1dCBlbmltIGVsZWlmZW5kLCBhbGlxdWV0IGludGVyZHVtIGxpZ3VsYSB0ZW1wb3IuIENyYXMgc2VkIG9ybmFyZSBlcm9zLiBBbGlxdWFtIHZ1bHB1dGF0ZSBhYyBwdXJ1cyBlZ2V0IGxhb3JlZXQuIEludGVnZXIgdGVtcHVzIGV4IHVybmEsIGlkIHBvc3VlcmUgcmlzdXMgbGFjaW5pYSBlZ2V0LiBTdXNwZW5kaXNzZSBpZCB0b3J0b3IgZW5pbS4gUGVsbGVudGVzcXVlIHRyaXN0aXF1ZSBkaWFtIGVmZmljaXR1ciwgbWF4aW11cyBhdWd1ZSBhLCB0aW5jaWR1bnQgc2VtLiBGdXNjZSBsdWN0dXMgbnVsbGEgYXVjdG9yLCBpbnRlcmR1bSBlbmltIHF1aXMsIGVsZWlmZW5kIG1pLiBNYWVjZW5hcyBpbiBzYXBpZW4gdG9ydG9yLg0KDQpgYGB7ciBmaWcud2lkdGg9OH0NCkFsbFRpbWVGVG1hICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhGVG1fMzYpLCBkZXNjKEZULikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSByZW9yZGVyKFBsYXllciwgZGVzYyhGVG1fMzYpLCBGVU49bWVhbikpICU+JQ0KICAgIGhlYWQobj0xMCkgJT4lDQogICAgZ2dwbG90KGFlcyhncm91cCA9IDEpKSArDQogICAgZ2VvbV9zZWdtZW50KGFlcyh4PVBsYXllciwgeGVuZD1QbGF5ZXIsIHk9RlRtXzM2LCB5ZW5kPUZUYV8zNiksIGNvbG9yPSJibGFjayIpICsNCiAgICBnZW9tX3BvaW50KGFlcyhQbGF5ZXIsIEZUbV8zNiwgY29sb3I9IiNGRjU4MDAiKSwgc2l6ZT01KSArDQogICAgZ2VvbV9wb2ludChhZXMoUGxheWVyLCBGVGFfMzYsIGNvbG9yPSIjRkY1ODAwIiksIHNpemU9Mi41KSArDQogICAgZ2VvbV9wb2ludChhZXMoUGxheWVyLCBGVG1fRywgY29sb3I9IiMwMDlkZmYiKSwgc2l6ZT01LCBzaGFwZT0xOCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsNCiAgICBnZ3RpdGxlKCJGcmVlIFRocm93IE1hZGUgcGVyIDM2IE1pbnV0ZXMgdnMuIEZyZWUgVGhyb3cgTWFkZSBwZXIgR2FtZSIpICsNCiAgICB4bGFiKCIiKSArDQogICAgeWxhYigiRlQgTWFkZSIpICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwgdmFsdWVzPWMoIiMwMDlkZmYiLCAiI0ZGNTgwMCIpLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiRlQgYS9tIHBlciBHYW1lIiwgIkZUIGEvbSBwZXIgMzYgbWludXRlcyIpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlPVRSVUUpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykNCmBgYA0KDQpTaG9ydGVyIGxpbmUgbWVhbnMgYmV0dGVyIHBlcmNlbnRhZ2UsIGFuZCB2aWNlIHZlcnNhLg0KDQo8YnIvPg0KDQo8L2Rpdj4NCg0KPGJyLz4NCjxici8+DQoNCiMjIyBCSUcgRnJlZSBUaHJvd2VyIE5hbWVzDQoNCjxkaXYgY2xhc3M9IkJveCI+DQoNCjxici8+DQoNClRoZSBGcmVlIFRocm93ZXJzIHdpbGwgYmUgc29ydGVkIGFjY29yZGluZyB0byB0aGUgZm9sbG93aW5nIGFyYnJpdGFyeSBjcml0ZXJpYSA6DQoNCiogQW5udWFsIGxlYWd1ZSBsZWFkZXIgaW4gRnJlZSBUaHJvdyBwZXJjZW50YWdlIGNhdGVnb3J5IGVhcm4gMSBwb2ludC4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gRnJlZSBUaHJvdyBwZXJjZW50YWdlIGNhdGVnb3J5IGVhcm4gMiBwb2ludHMuDQoqIFRvcCAxMCBhbGwtdGltZSBsZWFkZXJzIGluIEZyZWUgVGhyb3cgbWFkZSBjYXRlZ29yeSB3aXRoIEZUJSBtb3JlIHRoYW4gODUlIGVhcm4gMSBwb2ludC4NCiogVG9wIDEwIGFsbC10aW1lIGxlYWRlcnMgaW4gRnJlZSBUaHJvdyBtYWRlIHBlciAzNiBtaW51dGVzIGNhdGVnb3J5IHdpdGggRlQlIG1vcmUgdGhhbiA4NSUgZWFybiAxIHBvaW50Lg0KDQpDYWxjdWxhdGUgdGhlIHBvaW50cyBhbmQgd2UgaGF2ZSB0aGUgcmFuayENCg0KPGJyLz4NCg0KR2VuZXJhdGluZyBwbG90Li4uDQoNCmBgYHtyfQ0KQWxsVGltZUZUbSA8LSBOQkEgJT4lDQogICAgZ3JvdXBfYnkoUGxheWVyKSAlPiUNCiAgICBzdW1tYXJpc2UoRlRtID0gc3VtKFgzUCksDQogICAgICAgICAgICAgIEZUYSA9IHN1bShYM1BBKSwNCiAgICAgICAgICAgICAgRlQuID0gRlRtIC8gRlRhKSAlPiUNCiAgICBmaWx0ZXIoRlQuID4gMC44NSkgJT4lDQogICAgYXJyYW5nZShkZXNjKEZUbSkpICU+JSBoZWFkKDEwKQ0KDQpBbGxUaW1lRlRtMzYgPC0gQWxsVGltZUZUbWEgJT4lDQogICAgZmlsdGVyKEZULiA+PSAwLjg1KSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoRlRtXzM2KSwgZGVzYyhGVC4pKSAlPiUNCiAgICBoZWFkKG49MTApDQoNClN1cGVyRnJlZVRocm93ZXJzIDwtIGRhdGEuZnJhbWUoUGxheWVyID0gYyhhcy5jaGFyYWN0ZXIoVG9wRlQuJFBsYXllciksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHJlcChoZWFkKEFsbFRpbWVGVC4kUGxheWVyLCAxMCksIDIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoQWxsVGltZUZUbSRQbGF5ZXIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihBbGxUaW1lRlRtMzYkUGxheWVyKSkpICU+JQ0KICAgIGNvdW50KFBsYXllcikgJT4lDQogICAgYXJyYW5nZShkZXNjKG4pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHJlc3VsdDoNClN1cGVyRnJlZVRocm93ZXJzICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgbXV0YXRlKFBvcyA9IGdldG1vZGUoTkJBJFBvc2l0aW9uW05CQSRQbGF5ZXIgJWluJSBQbGF5ZXJdKSwNCiAgICAgICAgICAgRlRtID0gc3VtKE5CQSRGVFtOQkEkUGxheWVyICVpbiUgUGxheWVyXSksDQogICAgICAgICAgIEZUYSA9IHN1bShOQkEkRlRBW05CQSRQbGF5ZXIgJWluJSBQbGF5ZXJdKSwNCiAgICAgICAgICAgIkZUJSIgPSByb3VuZChGVG0vRlRhLCAzKSwNCiAgICAgICAgICAgVG9rZW5zID0gcGFzdGUoc3RycmVwKCJ8IiwgbikpLA0KICAgICAgICAgICBUb3RhbFRva2VucyA9IG4pICU+JQ0KICAgIHNlbGVjdCgtIm4iKSAlPiUNCiAgICBrYWJsZShlc2NhcGUgPSBGQUxTRSwgY2FwdGlvbiA9ICJUaGUgTW9zdCBQcm9taW5lbnQgRnJlZS1UaHJvd2VycyIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJjZW50ZXIiKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxLCBib2xkID0gVCkgJT4lDQogICAgY29sdW1uX3NwZWMoNiwgYm9sZCA9IFQsIGNvbG9yID0gImdvbGQiKSAlPiUNCiAgICBjb2x1bW5fc3BlYyg3LCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiM3Nzc3NzciKSAlPiUNCiAgICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KYGBgDQoNCjxici8+DQoNCmBgYHtyIGZpZy53aWR0aCA9IDl9DQpTdXBlckZyZWVUaHJvd2VycyAlPiUgd29yZGNsb3VkMihzaXplPTAuNCwgY29sb3I9J3JhbmRvbS1saWdodCcsIGJhY2tncm91bmRDb2xvcj0iYmxhY2siKQ0KYGBgDQoNCjxjZW50ZXI+PGltZyBzcmM9Imh0dHBzOi8vaW1hZ2UuaWJiLmNvL25zWWJYQS9GVE5hbWVzLmpwZyIgYWx0PSJkcmF3aW5nIiB3aWR0aD0iODgwcHgiLz48L2NlbnRlcj4NCg0KPGJyLz4NCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KDQojIyMgRnJlZSBUaHJvdyBSYXRpbyAmIFJhdGUNCg0KPGRpdiBjbGFzcz0iQm94Ij4NCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHNlZSB0aGUgcmF0aW8gYW5kIHRoZSByYXRlIG9mIEZyZWUtVGhyb3cuIFRoZSBmaXJzdCBvbmUgaXMgc2ltaWxhciB0byB3aGF0IEkgZGlkIHByZXZpb3VzbHksIGZpZ3VyaW5nIG91dCBob3cgbWFueSBwZXJjZW50IG9mIHRoZSBwb2ludHMgZWFybmVkIGZyb20gdGhlIEZyZWUgVGhyb3csIGFuZCB0aGVuIHNvcnRlZCBpdCBieSBoaWdoZXN0IEZyZWUtVGhyb3cgcmF0aW8uDQoNCmBgYHtyIGZpZy53aWR0aD05fQ0KTkJBICU+JQ0KICAgIGZpbHRlcihGVCA+IDEpICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zaXRpb24pLA0KICAgICAgICAgICAgICBUZWFtID0gZ2V0bW9kZShUbSksDQogICAgICAgICAgICAgIEdhbWVzID0gc3VtKEcpLA0KICAgICAgICAgICAgICBQdHMyID0gc3VtKFgyUCkgKiAyLA0KICAgICAgICAgICAgICBQdHMzID0gc3VtKFgzUCwgbmEucm0gPSBUKSAqIDMsDQogICAgICAgICAgICAgIFB0c0ZUID0gc3VtKEZUKSwNCiAgICAgICAgICAgICAgUFRTID0gc3VtKFBUUyksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKFBUUyAvIEdhbWVzLCAxKSwNCiAgICAgICAgICAgICAgUHRzMnIgPSByb3VuZChQdHMyIC8gUFRTLCAzKSwNCiAgICAgICAgICAgICAgUHRzM3IgPSByb3VuZChQdHMzIC8gUFRTLCAzKSwNCiAgICAgICAgICAgICAgUHRzRlRyID0gcm91bmQoUHRzRlQgLyBQVFMsIDMpLA0KICAgICAgICAgICAgICBGVG0gPSBzdW0oRlQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBKSwNCiAgICAgICAgICAgICAgRlQuID0gcm91bmQoRlRtIC8gRlRhLCAzKSkgJT4lDQogICAgZmlsdGVyKEZUbSA+IDEyMDApICU+JQ0KICAgIHNlbGVjdCgtYyhGVG0sIEZUYSwgUHRzMjpQdHNGVCkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhQdHNGVHIpKSAlPiUNCiAgICBtdXRhdGUoUGxheWVyID0gcmVvcmRlcihQbGF5ZXIsIFB0c0ZUciwgRlVOPW1lYW4pKSAlPiUNCiAgICBoZWFkKDEwKSAlPiUNCiAgICBnYXRoZXIoUGFyYW1ldGVyLCBWYWx1ZSwgUHRzMnI6UHRzRlRyKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKFBsYXllciwgVmFsdWUsIGZpbGw9Zm9yY2F0czo6ZmN0X3JldihQYXJhbWV0ZXIpKSkgKw0KICAgIGdlb21fYmFyKCBzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIikgKw0KICAgIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gcGFzdGUoVmFsdWUgKiAxMDAsICIlIikpLA0KICAgICAgICAgICAgICAgc3RhdCA9ICdpZGVudGl0eScsDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgIGZpbGwgPSAiI0ZGNDAwMCIsDQogICAgICAgICAgICAgICBjb2xvciA9ICIjMDBGRkZGIiwNCiAgICAgICAgICAgICAgIHNpemUgPSAyLjUpICsNCiAgICBnZ3RpdGxlKCJIaWdoZXN0IEZyZWUgVGhyb3cgUmF0aW8iKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICB4bGFiKCIiKSArDQogICAgeWxhYigiU2hvb3RpbmciKSArDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQo8YnIvPg0KDQpXaGlsZSBpbiB0aGUgMy1Qb2ludHMgUmF0aW8gd2Ugd2lsbCBzZWUgdGhlIGhpZ2hlc3QgcGVyY2VudGFnZSBwb2ludHMgY2FtZSBmcm9tIDMtUG9pbnRzIGlzIDc5LjMlLCBmb3IgRnJlZSBUaHJvdyB0aGUgaGlnaGVzdCBpcyAzOSUsIG9yIGFib3V0IGhhbGYuIFRoaXMgaXMgYmVjYXVzZSBpbiBGcmVlIFRocm93IGVhY2ggRlQgbWFkZSBhIHBsYXllciBvbmx5IGVhcm4gMSBwb2ludCwgY29udHJhc3QgdGhhdCB0byAzLXBvaW50cy4NCg0KVGhhdCdzIHdoeSBpdCdzIGEgZ29vZCBpZGVhIHRvIGxvb2sgYXQgaXQgZnJvbSBkaWZmZXJlbnQgYW5nbGUsIHVzaW5nIGRhdGEgcHJvdmlkZWQgYnkgdGhpcyBkYXRhc2V0LCB3ZSBhcmUgZ29pbmcgdG8gc2VlIHRoZSByYXRlIG9mIGZyZWUgdGhyb3dzIGF0dGVtcHRlZCBwZXIgZmllbGQgZ29hbCBhdHRlbXB0ZWQgKGBGVHJgKS4gQW5kIHRoZW4sIGFnYWluLCB3ZSBzb3J0ZWQgaXQgYnkgdGhlIEZUIHJhdGUuDQoNCkluIEZUIHJhdGlvIGFib3ZlLCB3ZSBnZXQgdGhlIG51bWJlcnMgZnJvbSBGVCBtYWRlIGFuZCBjb21wYXJlZCBpdCB3aXRoIDItcG9pbnRzIEZHIG1hZGUgdGltZXMgMiBhbmQgMy1wb2ludHMgRkcgbWFkZSB0aW1lcyAzLiBJbiBGcmVlIFRocm93IHJhdGUsIHdlIHRha2UgdGhlIG51bWJlciBvZiBGcmVlIFRocm93IGF0dGVtcHRzIGluIGNvbXBhcmlzb24gd2l0aCB0aGUgbnVtYmVyIG9mIEZpZWxkIEdvYWwgYXR0ZW1wdHMgKHdoaWNoIGlzIGluY2x1ZGVzIDMtUG9pbnRzIEZHKSwgbm8gbXVsdGlwbGljYXRpb24gb24gdGhlIG51bWJlciwgZWFjaCBzaG9vdGluZyBhdHRlbXB0IHRyZWF0ZWQgZXF1YWwuIA0KDQo8YnIvPg0KDQpgYGB7ciBmaWcud2lkdGggPSA5LCB3YXJuaW5nPUZBTFNFfQ0KRlRyYXRlQVZHIDwtIE5CQSAlPiUNCiAgICBzdW1tYXJpc2UoUGxheWVyID0gIkF2ZXJhZ2UiLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGVGEgPSBzdW0oRlRBLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGR2EgPSBzdW0oRkdBLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgICBGVGFwRyA9IHJvdW5kKEZUYSAvIEdhbWVzLCAyKSwNCiAgICAgICAgICAgICAgRkdhcEcgPSByb3VuZChGR2EgLyBHYW1lcywgMiksDQogICAgICAgICAgICAgIEZUciA9IHJvdW5kKEZUYSAvIEZHYSwgMykpICU+JQ0KICAgIHNlbGVjdCgtYyhHYW1lcywgRkdhLCBGVGEpKQ0KDQpGVHJhdGUgPC0gTkJBICU+JQ0KICAgIGdyb3VwX2J5KFBsYXllcikgJT4lDQogICAgc3VtbWFyaXNlKFBvcyA9IGdldG1vZGUoUG9zKSwNCiAgICAgICAgICAgICAgVGVhbSA9IGdldG1vZGUoVG0pLA0KICAgICAgICAgICAgICBHYW1lcyA9IHN1bShHKSwNCiAgICAgICAgICAgICAgUFRTID0gc3VtKFBUUyksDQogICAgICAgICAgICAgIFBQRyA9IHJvdW5kKFBUUyAvIEdhbWVzLCAxKSwNCiAgICAgICAgICAgICAgRkdhID0gc3VtKEZHQSksDQogICAgICAgICAgICAgIEZUYSA9IHN1bShGVEEpLA0KICAgICAgICAgICAgICBGVHIgPSByb3VuZChGVGEgLyBGR2EsIDMpLA0KICAgICAgICAgICAgICBGVGFwRyA9IHJvdW5kKEZUYSAvIEdhbWVzLCAyKSwNCiAgICAgICAgICAgICAgRkdhcEcgPSByb3VuZChGR2EgLyBHYW1lcywgMikpICU+JQ0KICAgIGZpbHRlcihGVGEgPiAxMjAwKSAlPiUNCiAgICBtdXRhdGUoUmFuayA9IG1pbl9yYW5rKGRlc2MoRlRyKSkpICU+JQ0KICAgIHNlbGVjdChSYW5rLCBldmVyeXRoaW5nKCkpICU+JQ0KICAgIGFycmFuZ2UoUmFuaykgJT4lDQogICAgaGVhZCgxMCkgJT4lDQogICAgYmluZF9yb3dzKEZUcmF0ZUFWRykNCg0KTGFiZWxGaXggPC0gRlRyYXRlICU+JQ0KICAgIG11dGF0ZShGVGFwRyA9IEZUYXBHLA0KICAgICAgICAgICBGVHIgPSBwYXN0ZShGVHIgKiAxMDAsICIlIikpICU+JQ0KICAgIGdhdGhlcihQYXJhbWV0ZXIsIENvdW50LCBGVGFwRywgLWMoUGxheWVyLCBGVHIpKSAlPiUNCiAgICBtdXRhdGUoUGxheWVyID0gZmFjdG9yKFBsYXllciwgbGV2ZWxzID0gdW5pcXVlKFBsYXllcltvcmRlcihkZXNjKEZUcikpXSkpKQ0KDQpGVHJhdGUgJT4lDQogICAgZ2F0aGVyKFBhcmFtZXRlciwgQ291bnQsIEZHYXBHOkZUYXBHLCAtYyhQbGF5ZXIsIEZUcikpICU+JQ0KICAgIG11dGF0ZShQbGF5ZXIgPSBmYWN0b3IoUGxheWVyLCBsZXZlbHMgPSB1bmlxdWUoUGxheWVyW29yZGVyKGRlc2MoRlRyKSldKSkpICU+JQ0KICAgIGdncGxvdChhZXMoUGxheWVyLCBDb3VudCwgZmlsbCA9IFBhcmFtZXRlciwgbGFiZWwgPSBGVHIpKSArDQogICAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogICAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBGVHIsIHkgPSAwLjcpLA0KICAgICAgICAgICAgICBkYXRhID0gTGFiZWxGaXgsDQogICAgICAgICAgICAgIHN0YXQgPSAnaWRlbnRpdHknLA0KICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJzdGFjayIsDQogICAgICAgICAgICAgIGZpbGwgPSAiI0ZGRkZGRiIsDQogICAgICAgICAgICAgIGNvbG9yID0gIiMwMDAwMDAiLA0KICAgICAgICAgICAgICBzaXplID0gNCkgKw0KICAgIGdndGl0bGUoIkZyZWUgVGhyb3cgUmF0ZSIpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgeGxhYigiIikgKw0KICAgIHlsYWIoIkZUL0ZHIHBlciBnYW1lIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsIHZhbHVlcz1jKCIjMDAwMEZGIiwgIiNGRjAwMDAiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkZHIHBlciBHYW1lIiwgIkZUIHBlciBHYW1lIikpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpDQogICAgDQpgYGANCg0KPGJyLz4NCjxici8+DQoNCjwvZGl2Pg0KDQo8YnIvPg0KPGJyLz4NCg0KLS0tDQoNCjxkaXYgY2xhc3M9InJvdyI+DQoNCiA8ZGl2IGNsYXNzPSJjb2x1bW4gbGVmdCI+DQogPGEgaHJlZj0iaHR0cHM6Ly9ycHVicy5jb20vbmluamF6emxlL05CQVNlYXNvbnMyIiB0YXJnZXQ9Il9ibGFuayI+DQo8YnV0dG9uIGNsYXNzPSJsZWZ0YnRuIj48L2J1dHRvbj4NCiA8L2E+DQogPC9kaXY+DQogDQogPGRpdiBjbGFzcz0iY29sdW1uIG1pZGRsZSI+DQoNCkVuZCBvZiBTZXNzaW9uDQoNCiAgPC9kaXY+DQogIA0KICA8ZGl2IGNsYXNzPSJjb2x1bW4gcmlnaHQiPg0KICA8YSBocmVmPSJodHRwczovL3JwdWJzLmNvbS9uaW5qYXp6bGUvTkJBU2Vhc29uczQiIHRhcmdldD0iX2JsYW5rIj4NCjxidXR0b24gY2xhc3M9InJpZ2h0YnRuIj48L2J1dHRvbj4NCiAgPC9hPg0KICA8L2Rpdj4NCiAgDQo8L2Rpdj4NCg0KLS0t