Objectives of this tutorial

In this tutorial, we’ll be using a dataset from the recent 2014-2016 West African Ebola outbreak. A large motivating factor for using this dataset, aside from the recency of the outbreak, is that the data has been made available for public use, along with the accompanying publication. Feel free to go beyond this tutorial to explore the data and refer to the publication, and it’s supplemental materials, to obtain a more detailed understanding of the contextual factors and major findings from these data.

The objectives of this tutorial are to help you visualize this outbreak dataset in R. We’ll stick to some very basic visualizations that are common (and more commonly found together) in genomic epidemiology research. More specifically this tutorial will show you how to:

  1. Create a phylogenetic tree with annotations (coloured node tips)

  2. Create a interactive map to show the geographic distribution of cases

  3. Create an interactive timeline plot

  4. Create a shiny application with interactive co-ordination between the above three data visualizations

Some data munging is required to extract metadata for this tutorial, however as the emphasis is data visualization this munging is shown at the very end of the tutorial.

The fully released shiny application and all of the visualization code are publicly available.

A (very brief) Introduction to Shiny

Shiny is a web application framework for R - it let’s you build interactive data visualizations. There are many excellent and in depth tutorials about Shiny and the universe of tools that interact with it. I’m not going to cover all of that, you can refer to the reference material on Shiny.

The main thing to understand about Shiny are the different parts of the shiny application: the user interface (ui), the server script, and the global shared variables. These are correspondingly represented as R scripts referred to as: ui.R, server.R, and global.R. It is these three R scripts in which the magic happens.

One nice aspect of using shiny and R is that you can link interactive visualizations to epidemiological, phylogenetic, and/or statistical analysis using a common syntax, which can be harder to do with other applications.

Implementing design patterns

In the lecture we talked about why-what-how paradigm for thinking through data visualizations. This tutorial is inspired by some common design patterns that are shared between applications.

We’re going to draw from NextStrain and Microreact for inspiration. Both of these visualizations use the Ebola dataset that we also use in the tutorial (at a minimum).

These visualization share in common:

We can see, however, some interesting differences. For example, the ways that you use the timeline to filter data in the three and map and the way that data is displayed on the maps and trees.

We’re going to implement some of the shared aspects in these data visualizations using Shiny.

Data

For this tutorial we will be using data, which has been made graciously made publicly available, from the Quick (2016) paper: “Real-time, portable genome sequencing for Ebola surveillance”, which is available on github.

For this tutorial, we are going to be using the phylogenetic tree that has been made available to us by Josh Quick and Nicholas Loman (Makona_728_cds_ig.MCC.tree. This tree is the Nexus format, which can be read into R directly. We’ll need to wrangle the data to extract some additional metadata for plotting, but since this is not the focus of this tutorial, we’ve put all the data munging we’ve done at the end.

Creating basic & static data visualizations

Here we’ll create the following data visualizations:

  1. Phylogenetic tree with annotations

  2. Interactive Map

  3. Interactive timeline plot

All the files are details are available online!

First, we load the libraries and tools that we’ll need

library(ape)
library(ggtree)
library(lubridate)
library(tidyr)
library(dplyr)
library(ggmap)
library(RColorBrewer)
library(dygraphs)
library(xts)
library(leaflet)
source("serverUtility.R") #custom bit of code I wrote with helper functions
source("global.R")  # this isn't necessary for basic data viz, but it'll come in handy in the shiny app

Lastly, I’vesaved the processed metadata as a file, which I’ll now load for the data visualizations.

metadata<-readRDS("./data/ebola_metadata.RDS")

Phylogenetic Tree

To plot the phylogenetic tree we’ll use the ape library to read the tree into R and ggtree library to add annotations to tree.

myTree <- read.nexus("./analysis/Makona.tree") 
tree<-ggtree(myTree)
tree<-colorTreeTip(tree,metadata,"Country")
tree

As an aside, the colorTreeTip function is one that I have created, and that resides in the serverUtility.R method. The code for that method is as follows:

#FUNCTION COLOR TREETIPE
colorTreeTip = function(tree,metadata,var) {
  tree<-tree %<+% metadata + geom_tippoint(aes_string(color=var),size=5, alpha=0.35) + theme(legend.position="right")
  if(var %in% c("Country")){
    #the drop=FALSE is necessary to maintain a consistent colour scale across several
    #visualizations. This will be important in the shiny application
    tree<-tree + scale_color_manual(values=as.character(countryCol$colVals),drop=FALSE)
  }
  
  tree #return the 
}

The above code provides a custom colour scale , when we are trying to annotate the phylogenetic tree with Country. You can also colour the tree by Region, but it is very hard to choose the right colour scheme for so many different regions, so we let R just pick colours of us.

tree<-ggtree(myTree)
tree<-colorTreeTip(tree,metadata,"Region")
tree

Map

We will now use the leaflet package to plot the locations of the different cases. Naively, we could just plot every single data point, but this is actually not very helpful. For privacy concerns, all data points are plotted to the region level, so we’d have a bunch of data points sitting on top of each other, which is not a very useful map.

Instead, we’ll use the dplyr package to count the total number of cases within a country or region, and just plot that instead. We’ll make the size of points on the plot roughly proportional to the total number of cases, we’ll also add some pop-up text to indicate the case number!.

# First, we need to create some new data
# Counting number cases per country
aggDat<-metadata %>%
       filter(Country !="?") %>%
       group_by(Country,country_lon,country_lat) %>%
       dplyr::count()%>%
       mutate(popup=sprintf("%s = %d cases",Country,n)) #create a popup for the map
# Here's a very quick look at what this command generates for us:
aggDat

Now we’ll plot the actual map using the data we dervied (the aggData data.frame)

# Now, we'll create the Map
# This first command will creat an empty map
m<-leaflet(aggDat)
  m %>%
        addTiles()%>% 
        addCircleMarkers(
          lng=~country_lon,
          lat= ~country_lat,
          radius=~sqrt(n)*2,
          color = ~pal(Country),
          stroke = FALSE, fillOpacity = 0.7,
          label=~as.character(popup),
          labelOptions = labelOptions(noHide = T),
          options = leafletOptions(minZoom = 0, maxZoom = 10,scroolWheelZoom=FALSE))

By modifying the code very slightly, we can also plot data regionally as opposed the country level.

aggDat<-metadata %>%
        filter(Country !="?") %>%
        group_by(Country,Region,region_lon,region_lat) %>%
        dplyr::count()%>% 
        mutate(popup=sprintf("%s (%s) = %d cases",Region,Country,n))
      
m<-leaflet(aggDat)
      
m %>%
  addTiles()%>% 
  addCircleMarkers(
    lng=~region_lon,
    lat= ~region_lat,
    radius=~sqrt(n)*2,
    color = ~pal(Country), #we actually colour the points by country here
    stroke = FALSE, fillOpacity = 0.7,
    label=~as.character(popup),
    labelOptions = labelOptions(noHide = F)
  )
Data contains 3 rows with either missing or invalid lat/lon values and will be ignored

Leaflet also allows you to automatically generate clusters that will vary when you zoom in and out on the map. We didn’t implement this feature in the shiny application, but I wanted to let you know this is available.

m<-leaflet(metadata) 
# by providing the region latitude and longtitude co-ordinates we allow clustering of regional samples
m %>%
  addTiles()%>%
  addCircleMarkers(
    lng=~region_lon,
    lat= ~region_lat,
    stroke = FALSE, fillOpacity = 0.5,
    clusterOptions= markerClusterOptions(titile="regional clusters") #cluster options
  )
Data contains 34 rows with either missing or invalid lat/lon values and will be ignored

The cluster colour here are automatically assigned by the Leaflet package, and are as follows:

  • orange / red for the largest clusters
  • yellow for “medium” clusters
  • green for small clusters

By click on a point, the clustering algorithm reveals the geospatial locations that were aggregated into a cluster (which its a neat and handy feature).

Timeline

Finally, we will use the dygraphs package to create a timeline plot that shows the aggregate number of cases over time in a country. The dygraphs package requires an xts timeseries object to create the plots, so we’ll use the xts package in addition to dplyr to create the time series.

First, let’s set up the timeseries, which is the aggregate monthly number of cases per country. The metadata has the sample collection date (year-month-day), but to get the monthly aggregate sum we need wrangle the data a little bit more.

First, let’s make the timeseries, by wrangling the data a bit:

#count cases by date, we're also going to aggregatge by *month* so we're going to 
    #create a new time variable
    timeseriesData<-metadata %>%
      mutate(yearMonth=ymd(sapply(YearMonth,function(x){paste(x,"01",sep="-")}))) %>% 
      group_by(yearMonth)%>% 
      dplyr::count(Country) %>%
      complete(yearMonth,Country) %>% #make sure that all dates are represented
      mutate(n=replace(n,is.na(n),0)) #turn NAs from above command in zeros

Next, let’s take that time series and turn it into an xts object

    
#create an xts object
xtsObj<-c()
  for(i in unique(timeseriesData$Country)){
    temp<-timeseriesData %>%
      filter(Country == i)
    
    xtsObj<-cbind(xtsObj,xts(temp$n, temp$yearMonth))
  }
    
#name out object, so that it plots the time series correctly
colnames(xtsObj)<-unique(timeseriesData$Country)

As final step, we can now create the dygraph. This graph is interactive; you can interact with the dygraph by change the range on the slider below, or by highlighting a region of the dyrgraph with your mouse.

#now make the the dygraph (yay!)
dygraph(xtsObj) %>% 
  dyOptions(stackedGraph = TRUE,colors = countryCol$colVals) %>%
  dyRangeSelector(fillColor="#c97f91",strokeColor="#c97f91")

Putting things together in the shiny application

The above code could be used to create a shiny application, but all you’d have are three images that don’t really interact with each other. In this part, I’ll show you how you can create links between the visualizations to allow for co-ordinated interactions.

The full shiny application we’re working towards can be tested out here: [https://amcrisan.shinyapps.io/EpiDesignPattern/](https://amcrisan.shinyapps.io/EpiDesignPattern/)

The main interaction driver in the application is dygraph timeline. You can highlight a region of the timeline and the phylogenetic tree & map with change accordingly.

Working with ui.R and server.R

Earlier in this document, we introduced the three main files shiny files: ui.R, server.R, and global.R. Here I’ll show how, for our example, we would use ui.R and server.R in particular. I’m using a package called shinydashboard for the visual appearance of my shiny application, which is very similar to the default ui.R code, but with some slight differences.

Ok - so, let’s just imagine a very simple shiny app : it just shows a phylogenetic tree and a user can choose to annotate the tips according to to country or region. Here’s what this little bit of shiny code would look like.

ui.R

dashboardPage(skin = "black",
  dashboardHeader(),
  dashboardSidebar(
    h2("Tree Options"),
    selectizeInput(inputId="colorBy",
                   label="Color By",
                   choices=c("Date","Country","Region"),
                   multiple=FALSE,
                   selected="Country")
  ),
  dashboardBody(
    tags$head(
      tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")
    ),
    plotOutput("treePlot")
  )
)

In the above code selectizeInput creates a dropdown list, which indicates whether the tree should be coloured by country, region, or date. It will store this information in a variable called colorBy. The phylogenetic tree will be plotted by the plotOutput function (this works here because ggtree, which create the phylogenetic tree, is a ggplot data type), and will the coloured according to whatever variable is indicated in by the colorBy variable.

Importantly: selectiveInput takes data from the user via a drop down list and provides these data to server.R as an input variable. Conversely, plotOutput takes a resulting graph, which is produced in server.R, hence it is an ouput variable.

Here’s what the server code would look like to bring this plot to life:

server.R

shinyServer(function(input, output) {

  #we store treePlot as an output variable
  output$treePlot <- renderPlot({
      
      # We're going to load trees that have already been stored.
      # The alternative is to re-compute tree each time, which can be slow
      # Since we're keeping the base structure, it's good to 
     
      tree<-readRDS("./data/ebolaTree.RDS")  # default is rooted tree
  
      #we'll get the value from the colorBy variable to decided
      #how to plot the tree tip. 
      tree<-colorTreeTip(tree,metadata,input$colorBy)
      #return the tree
      tree
  })
})

So, how does this all work together? Well, the treePlot will generate when the shiny application is started and will colour its tips by using the default colorBy value (Country). When the user picks a different value from selectizeInput, shiny with automatically redraw the tree annotations.

Reactive data

For more complex applications that have more moving parts that need to co-ordinate, we need a more complicated data type : a reactive data objects

For this application, I’ve made a decision: all the interactions will be based on the timeline graph. So, I want a dataset that changes according to the date range. This reactive dataset would live inside server.R. So, I’ll add the timeseries to ui.R, and I’ll we’ll see how the server.R code will change.

ui.R

dashboardPage(skin = "black",
  dashboardHeader(),
  dashboardSidebar(
    h2("Tree Options"),
    selectizeInput(inputId="colorBy",
                   label="Color By",
                   choices=c("Date","Country","Region"),
                   multiple=FALSE,
                   selected="Country")
  ),
  dashboardBody(
    tags$head(
      tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")
    ),
    plotOutput("treePlot"), #here's our phylogenetic tree
    dygraphOutput("timeline") #here's our timeline, it affect the metadataReactive variable
  )
)

And now the server code:

server.R

shinyServer(function(input, output) {

  # metadata variable that changes reactive according to the
  # timeline date range
  metadataReactive <- reactive({
    startDate<-input$timeline_date_window[[1]]
    endDate<-input$timeline_date_window[[2]]
    
    if(is.null(startDate)){
      metadata #when program initializes, start date will be null, so we send up the full dataset
    }else{
      metadata %>% filter(Date>=startDate & Date <= endDate)
    }
  })
  #.
  #.
  #.
  #not included: phylogenetic tree code, timeline dygraph code

})

Ok - so we get some information from the dygraph about the date range (input$timeline_date_window[[1]] and input$timeline_date_window[[2]]). In this example, the dygraph (and output variable called timeline) can also provide inputs (denoted by _date_window) to other output variables. When you interact with the timeline visualization (the dygraph), shiny will quietly collect _date_window data and pass this information to our reactive data type, metadataReactive.

Making our data visualizations reactive

OK! So earlier we created the static versions of our data visualizations, now we’ll create the interactive versions of our data visualizations. In the area below, I’ve provided the code that needs to go into the server.R file, but I will also indicate the complementary command that should be in ui.R to output the visualization.

Phylogenetic Tree

For the phylogenetic tree, we only want to annotate items in the phylogenetic tree based upon the cases that fall into the date range we’ve selected in the timeline plot and colour the tree tips based upon the colorBy variable. To output this phylogenetic tree we use the plotOutput function in ui.R. We also need to add some extra steps to get our tree code to work correctly with the reactive metadata (metadataReactive). This what it would look like:

output$treePlot <- renderPlot({
    
    # We're going to load trees that have already been stored.
    # The alternative is to re-compute tree each time, which can be slow
    # Since we're keeping the base structure, it's good to 
    tree<-readRDS("./data/ebolaTree.RDS")  # default is rooted tree
    
    #metadata is available as a global variable, so we don't need to load it
    #but we've also created this reactive variable, so we're going to 
    #also colour by.
    # We don't need all the data, just some
    colTreeMeta<-metadata[,c("ID","Country","Region","Date")] 
    colTreeMeta$Country<-factor(colTreeMeta$Country,levels=c(levels(colTreeMeta$Country),"")) #this is a hack to avoid a ggtree error
    
    # to access the reactive data, we need to call it as function
    temp<-metadataReactive() #this is now a data.frame
    
    #this is a way I've choosen to "hide" tree tips that are not present
    #in the date range specificed by the time series plot. 
    colTreeMeta<-colTreeMeta %>%
      mutate(Country = replace(Country,!(ID %in% temp$ID),""))
    
    #instead of metadata, I will pass colTreeMeta instead of metadata
    tree<-colorTreeTip(tree,colTreeMeta,input$colorBy)
  
    
    #return the tree
    tree
  })

As you can see, the code to make a tree interactive and listening for interactions with other trees is more complicated that static trees and requires a little extra data wrangling. Every time metadataReactive is changed (due to interactions with the timeseries) the phylogenetic tree is automatically regenerated thanks to Shiny’s internal logic that automatically detects such dependencies.

Map

We’re only going to show map points, and aggregated sums, based upon the cases that occurred without a specified time period. We also want to change whether data are aggregated at the Country level, or the region level (affected by the colorBy variable). To output this map we use the leafletOutput function in ui.R. Here’s how the interactive map would be coded up.

 output$caseMap<-renderLeaflet({
     m<-NULL
    
    if(input$colorBy=="Country" | input$colorBy=="Date"){

      aggDat<-metadataReactive() %>%
        filter(Country !="?") %>%
        group_by(Country,country_lon,country_lat) %>%
        dplyr::count()%>% 
        mutate(popup=sprintf("%s = %d cases",Country,n))
      
      m<-leaflet(aggDat) 
      
      m %>%
        addTiles()%>% 
        addCircleMarkers(
          lng=~country_lon,
          lat= ~country_lat,
          radius=~sqrt(n)*2,
          color = ~pal(Country),
          stroke = FALSE, fillOpacity = 0.7,
          label=~as.character(popup),
          labelOptions = labelOptions(noHide = T)
        )
    }else if(input$colorBy=="Region"){

      aggDat<-metadataReactive() %>%
        filter(Country !="?") %>%
        group_by(Country,Region,region_lon,region_lat) %>%
        dplyr::count()%>% 
        mutate(popup=sprintf("%s (%s) = %d cases",Region,Country,n))
      
      m<-leaflet(aggDat)
      
      m %>%
        addTiles()%>% 
        addCircleMarkers(
          lng=~region_lon,
          lat= ~region_lat,
          radius=~sqrt(n)*2,
          color = ~pal(Country),
          stroke = FALSE, fillOpacity = 0.7,
          label=~as.character(popup),
          labelOptions = labelOptions(noHide = F)
        )
    }
    
  })

The map plotting code is identical to the static version, except it uses metadataReactive() instead of metadata. Every time metadataReactive changes, the map will automatically be updated.

Timeline

The code for the timeline will actually stay exactly that same! The ui.R element that will output the dygraph timeseries is dygraphOutput. The dataset for the timeseries is constant (doesn’t change), so its exactly the same code that we use for the static version.

Final Shiny Application Code

That’s it! The full code for the server.R and ui.R is available in the github repository for this tutorial.

Preparing the data for analysis

In this last section, I’ve provided the nity gritty details of what it took to extract the metadata from the phylogenetic tree to make it usable in R.

myTree <- read.nexus("./analysis/Makona.tree") 

pTree<-ggtree(myTree)

# I also want to get out the metadata 

rawMeta<-get.tree(myTree)$tip.label # for these files all the INFO is the tree node label

rawMeta<-sapply(rawMeta,function(x){strsplit(x,"\\|")} %>% unlist()) %>% t()
rawMeta<-cbind(rownames(rawMeta),rawMeta)

colnames(rawMeta)<-c("ID","ORG","LAB","LAB2","LAB3","Country","Region","Region2","Protocol","Date") #now its a data frame

metadata<-data.frame(rawMeta)

#formatting the date a little bit
metadata$Date<-ymd(as.character(metadata$Date))
metadata$YearMonth<-format(metadata$Date,"%Y-%m") #for quick access

#now lets draw some trees!
#very nice, the tree is coloured according to the country
pTree %<+% metadata + geom_tippoint(aes(color=YearMonth),size=5, alpha=0.35) + theme(legend.position="right")

#what about a radial tree? -yes, it works, awesome, ggtree is a great package
pTree<-ggtree(myTree,layout="circular")
pTree %<+% metadata+ geom_tippoint(aes(color=Country),size=5, alpha=0.35) + theme(legend.position="right")


#so to add geographic data, I need to do a bit more processing here to store co-ordinates
#I know the countries and Guinea, Sierra Leone, and Liberia, so I'll look those up
dfCountry<-data.frame(Country=c("GIN","SLE","LBR"),
                      longName=c("Guinea","Sierra Leone","Liberia"),
                      geocode(c("Guinea","Sierra Leone","Liberia"),output="latlon"))
colnames(dfCountry)<- c("Country","countryLongName","country_lon","country_lat")


#let's joint this data with the rest of the metadata, I'm going to need the long names to get the accurate region
#test2<-base::merge(x = metadata, y = dfCountry, by = "Country",all.x=T), problem is it effects ggtree

#for resons unknown to me, merge is doing something bad to the data frame, so I've have to implement this hack
mergedCountry<-sapply(metadata$Country,function(x){
  test<-dfCountry %>% filter(Country==as.character(x)) %>% select(countryLongName,country_lon,country_lat)
  c(as.character(test[1,1]),test[1,2],test[1,3])
}) %>% t() %>% data.frame

colnames(mergedCountry)<-c("countryLongName","country_lon","country_lat")


metadata<-cbind(metadata,mergedCountry)

regionString<-metadata %>% 
  filter(Region !='?') %>% 
  mutate(searchString = paste(Region,countryLongName,sep=", ")) %>% 
  select(Region,searchString) %>%
  unique()

dfRegion<-data.frame(Region = regionString$Region,
                     geocode(unique(regionString$searchString)))
colnames(dfRegion)<-c("Region","region_lon","region_lat")

#for resons unknown to me, merge is doing something bad to the data frame, so I've have to implement this hack
mergedRegion<-sapply(metadata$Region,function(x){
  test<-dfRegion %>% filter(Region==as.character(x)) %>% select(region_lon,region_lat)
  c(as.character(test[1,1]),test[1,2])
}) %>% t() %>% data.frame()
colnames(mergedRegion)<-c("region_lon","region_lat")

metadata<-cbind(metadata,mergedRegion)

#now I have to fix all of the number issues
metadata$country_lon<-as.numeric(as.character(metadata$country_lon))
metadata$country_lat<-as.numeric(as.character(metadata$country_lat))

metadata$region_lon<-as.numeric(as.character(metadata$region_lon))
metadata$region_lat<-as.numeric(as.character(metadata$region_lat))

saveRDS(file="data/ebola_metadata.RDS",metadata) #it all works now, that was annoying
LS0tCnRpdGxlOiAiQ0JXLTIwMTcgTW9kdWxlIDcgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKLS0tCgojIE9iamVjdGl2ZXMgb2YgdGhpcyB0dXRvcmlhbAoKSW4gdGhpcyB0dXRvcmlhbCwgd2UnbGwgYmUgdXNpbmcgYSBkYXRhc2V0IGZyb20gdGhlIHJlY2VudCAyMDE0LTIwMTYgV2VzdCBBZnJpY2FuIEVib2xhIG91dGJyZWFrLiBBIGxhcmdlIG1vdGl2YXRpbmcgZmFjdG9yIGZvciB1c2luZyB0aGlzIGRhdGFzZXQsIGFzaWRlIGZyb20gdGhlIHJlY2VuY3kgb2YgdGhlIG91dGJyZWFrLCBpcyB0aGF0IHRoZSBkYXRhIGhhcyBiZWVuIFttYWRlIGF2YWlsYWJsZSBmb3IgcHVibGljIHVzZV0oaHR0cHM6Ly9naXRodWIuY29tL25pY2tsb21hbi9lYm92KSwgYWxvbmcgd2l0aCB0aGUgW2FjY29tcGFueWluZyBwdWJsaWNhdGlvbl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9uYXR1cmUvam91cm5hbC92NTMwL243NTg5L2Z1bGwvbmF0dXJlMTY5OTYuaHRtbCkuIEZlZWwgZnJlZSB0byBnbyBiZXlvbmQgdGhpcyB0dXRvcmlhbCB0byBleHBsb3JlIHRoZSBkYXRhIGFuZCByZWZlciB0byB0aGUgcHVibGljYXRpb24sIGFuZCBpdCdzIHN1cHBsZW1lbnRhbCBtYXRlcmlhbHMsIHRvIG9idGFpbiBhIG1vcmUgZGV0YWlsZWQgdW5kZXJzdGFuZGluZyBvZiB0aGUgY29udGV4dHVhbCBmYWN0b3JzIGFuZCBtYWpvciBmaW5kaW5ncyBmcm9tIHRoZXNlIGRhdGEuCgpUaGUgb2JqZWN0aXZlcyBvZiB0aGlzIHR1dG9yaWFsIGFyZSB0byBoZWxwIHlvdSB2aXN1YWxpemUgdGhpcyBvdXRicmVhayBkYXRhc2V0IGluIFIuIFdlJ2xsIHN0aWNrIHRvIHNvbWUgdmVyeSBiYXNpYyB2aXN1YWxpemF0aW9ucyB0aGF0IGFyZSBjb21tb24gKGFuZCBtb3JlIGNvbW1vbmx5IGZvdW5kIHRvZ2V0aGVyKSBpbiBnZW5vbWljIGVwaWRlbWlvbG9neSByZXNlYXJjaC4gTW9yZSBzcGVjaWZpY2FsbHkgdGhpcyB0dXRvcmlhbCB3aWxsIHNob3cgeW91IGhvdyB0bzoKCjEuIENyZWF0ZSBhIHBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggYW5ub3RhdGlvbnMgKGNvbG91cmVkIG5vZGUgdGlwcykKCjIuIENyZWF0ZSBhIGludGVyYWN0aXZlIG1hcCB0byBzaG93IHRoZSBnZW9ncmFwaGljIGRpc3RyaWJ1dGlvbiBvZiBjYXNlcwoKMy4gQ3JlYXRlIGFuIGludGVyYWN0aXZlIHRpbWVsaW5lIHBsb3QKCjQuIENyZWF0ZSBhIHNoaW55IGFwcGxpY2F0aW9uIHdpdGggaW50ZXJhY3RpdmUgY28tb3JkaW5hdGlvbiBiZXR3ZWVuIHRoZSBhYm92ZSB0aHJlZSBkYXRhIHZpc3VhbGl6YXRpb25zCgpTb21lIGRhdGEgbXVuZ2luZyBpcyByZXF1aXJlZCB0byBleHRyYWN0IG1ldGFkYXRhIGZvciB0aGlzIHR1dG9yaWFsLCBob3dldmVyIGFzIHRoZSBlbXBoYXNpcyBpcyBkYXRhIHZpc3VhbGl6YXRpb24gdGhpcyBtdW5naW5nIGlzIHNob3duIGF0IHRoZSB2ZXJ5IGVuZCBvZiB0aGUgdHV0b3JpYWwuIAoKVGhlIFtgZnVsbHkgcmVsZWFzZWQgc2hpbnkgYXBwbGljYXRpb25gXShodHRwczovL2FtY3Jpc2FuLnNoaW55YXBwcy5pby9FcGlEZXNpZ25QYXR0ZXJuLykgYW5kIGFsbCBvZiB0aGUgW2B2aXN1YWxpemF0aW9uIGNvZGVgXShodHRwczovL2dpdGh1Yi5jb20vYW1jcmlzYW4vRXBpRGVzaWduUGF0dGVybikgYXJlIHB1YmxpY2x5IGF2YWlsYWJsZS4KCgojIEEgKHZlcnkgYnJpZWYpIEludHJvZHVjdGlvbiB0byBTaGlueQoKW1NoaW55XShodHRwczovL3NoaW55LnJzdHVkaW8uY29tLykgaXMgYSB3ZWIgYXBwbGljYXRpb24gZnJhbWV3b3JrIGZvciBSIC0gaXQgbGV0J3MgeW91IGJ1aWxkIGludGVyYWN0aXZlIGRhdGEgdmlzdWFsaXphdGlvbnMuIFRoZXJlIGFyZSBtYW55IGV4Y2VsbGVudCBhbmQgaW4gZGVwdGggdHV0b3JpYWxzIGFib3V0IFNoaW55IGFuZCB0aGUgdW5pdmVyc2Ugb2YgdG9vbHMgdGhhdCBpbnRlcmFjdCB3aXRoIGl0LiBJJ20gbm90IGdvaW5nIHRvIGNvdmVyIGFsbCBvZiB0aGF0LCB5b3UgY2FuIHJlZmVyIHRvIHRoZSByZWZlcmVuY2UgbWF0ZXJpYWwgb24gU2hpbnkuIAoKVGhlIG1haW4gdGhpbmcgdG8gdW5kZXJzdGFuZCBhYm91dCBTaGlueSBhcmUgdGhlIGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgc2hpbnkgYXBwbGljYXRpb246IHRoZSB1c2VyIGludGVyZmFjZSAodWkpLCB0aGUgc2VydmVyIHNjcmlwdCwgYW5kIHRoZSBnbG9iYWwgc2hhcmVkIHZhcmlhYmxlcy4gVGhlc2UgYXJlIGNvcnJlc3BvbmRpbmdseSByZXByZXNlbnRlZCBhcyBSIHNjcmlwdHMgcmVmZXJyZWQgdG8gYXM6IGB1aS5SYCwgYHNlcnZlci5SYCwgYW5kIGBnbG9iYWwuUmAuIEl0IGlzIHRoZXNlIHRocmVlIFIgc2NyaXB0cyBpbiB3aGljaCB0aGUgbWFnaWMgaGFwcGVucy4KCk9uZSBuaWNlIGFzcGVjdCBvZiB1c2luZyBzaGlueSBhbmQgUiBpcyB0aGF0IHlvdSBjYW4gbGluayBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucyB0byBlcGlkZW1pb2xvZ2ljYWwsIHBoeWxvZ2VuZXRpYywgYW5kL29yIHN0YXRpc3RpY2FsIGFuYWx5c2lzIHVzaW5nIGEgY29tbW9uIHN5bnRheCwgd2hpY2ggY2FuIGJlIGhhcmRlciB0byBkbyB3aXRoIG90aGVyIGFwcGxpY2F0aW9ucy4KCgojIEltcGxlbWVudGluZyBkZXNpZ24gcGF0dGVybnMKCkluIHRoZSBsZWN0dXJlIHdlIHRhbGtlZCBhYm91dCA8dHQ+d2h5LXdoYXQtaG93PC90dD4gcGFyYWRpZ20gZm9yIHRoaW5raW5nIHRocm91Z2ggZGF0YSB2aXN1YWxpemF0aW9ucy4gVGhpcyB0dXRvcmlhbCBpcyBpbnNwaXJlZCBieSBzb21lIGNvbW1vbiAqZGVzaWduIHBhdHRlcm5zKiB0aGF0IGFyZSBzaGFyZWQgYmV0d2VlbiBhcHBsaWNhdGlvbnMuIAoKCldlJ3JlIGdvaW5nIHRvIGRyYXcgZnJvbSBbTmV4dFN0cmFpbl0oaHR0cDovL3d3dy5uZXh0c3RyYWluLm9yZy9lYm9sYT9jPWRpdmlzaW9uJnI9ZGl2aXNpb24pIGFuZCBbTWljcm9yZWFjdF0oaHR0cHM6Ly9taWNyb3JlYWN0Lm9yZy9wcm9qZWN0L3dlc3QtYWZyaWNhbi1lYm9sYS1lcGlkZW1pYz90dD1yYykgZm9yIGluc3BpcmF0aW9uLiBCb3RoIG9mIHRoZXNlIHZpc3VhbGl6YXRpb25zIHVzZSB0aGUgRWJvbGEgZGF0YXNldCB0aGF0IHdlIGFsc28gdXNlIGluIHRoZSB0dXRvcmlhbCAoYXQgYSBtaW5pbXVtKS4KClRoZXNlIHZpc3VhbGl6YXRpb24gc2hhcmUgaW4gY29tbW9uOgoKKiBBIHBoeWxvZ2VuZXRpYyB0cmVlCgoqIEEgbWFwCgoqIEEgdGltZWxpbmUgb2YgZXZlbnRzCgpXZSBjYW4gc2VlLCBob3dldmVyLCBzb21lIGludGVyZXN0aW5nIGRpZmZlcmVuY2VzLiBGb3IgZXhhbXBsZSwgdGhlIHdheXMgdGhhdCB5b3UgdXNlIHRoZSB0aW1lbGluZSB0byBmaWx0ZXIgZGF0YSBpbiB0aGUgdGhyZWUgYW5kIG1hcCBhbmQgdGhlIHdheSB0aGF0IGRhdGEgaXMgZGlzcGxheWVkIG9uIHRoZSBtYXBzIGFuZCB0cmVlcy4gCgpXZSdyZSBnb2luZyB0byBpbXBsZW1lbnQgc29tZSBvZiB0aGUgc2hhcmVkIGFzcGVjdHMgaW4gdGhlc2UgZGF0YSB2aXN1YWxpemF0aW9ucyB1c2luZyBTaGlueS4gIAoKCiMjIERhdGEKCkZvciB0aGlzIHR1dG9yaWFsIHdlIHdpbGwgYmUgdXNpbmcgZGF0YSwgd2hpY2ggaGFzIGJlZW4gbWFkZSBncmFjaW91c2x5IG1hZGUgcHVibGljbHkgYXZhaWxhYmxlLCBmcm9tIHRoZSBRdWljayAoMjAxNikgcGFwZXI6IFsiUmVhbC10aW1lLCBwb3J0YWJsZSBnZW5vbWUgc2VxdWVuY2luZyBmb3IgRWJvbGEgc3VydmVpbGxhbmNlIl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9uYXR1cmUvam91cm5hbC92NTMwL243NTg5L2Z1bGwvbmF0dXJlMTY5OTYuaHRtbCksIHdoaWNoIGlzIGF2YWlsYWJsZSBvbiBbZ2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vbmlja2xvbWFuL2Vib3YpLgoKRm9yIHRoaXMgdHV0b3JpYWwsIHdlIGFyZSBnb2luZyB0byBiZSB1c2luZyB0aGUgcGh5bG9nZW5ldGljIHRyZWUgdGhhdCBoYXMgYmVlbiBtYWRlIGF2YWlsYWJsZSB0byB1cyBieSBKb3NoIFF1aWNrIGFuZCBOaWNob2xhcyBMb21hbiBbKE1ha29uYV83MjhfY2RzX2lnLk1DQy50cmVlXShodHRwczovL2dpdGh1Yi5jb20vbmlja2xvbWFuL2Vib3YvdHJlZS9tYXN0ZXIvcGh5bG8vYmVhc3QpLiBUaGlzIHRyZWUgaXMgdGhlIE5leHVzIGZvcm1hdCwgd2hpY2ggY2FuIGJlIHJlYWQgaW50byBSIGRpcmVjdGx5LiBXZSdsbCBuZWVkIHRvIHdyYW5nbGUgdGhlIGRhdGEgdG8gZXh0cmFjdCBzb21lIGFkZGl0aW9uYWwgbWV0YWRhdGEgZm9yIHBsb3R0aW5nLCBidXQgc2luY2UgdGhpcyBpcyBub3QgdGhlIGZvY3VzIG9mIHRoaXMgdHV0b3JpYWwsIHdlJ3ZlIHB1dCBhbGwgdGhlIGRhdGEgbXVuZ2luZyB3ZSd2ZSBkb25lIGF0IHRoZSBlbmQuIAoKCiMjIENyZWF0aW5nIGJhc2ljICYgc3RhdGljIGRhdGEgdmlzdWFsaXphdGlvbnMKCkhlcmUgd2UnbGwgY3JlYXRlIHRoZSBmb2xsb3dpbmcgZGF0YSB2aXN1YWxpemF0aW9uczoKCjEuIFBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggYW5ub3RhdGlvbnMKCjIuIEludGVyYWN0aXZlIE1hcAoKMy4gSW50ZXJhY3RpdmUgdGltZWxpbmUgcGxvdAoKCkFsbCB0aGUgZmlsZXMgYXJlIGRldGFpbHMgYXJlIFthdmFpbGFibGUgb25saW5lXShodHRwczovL2dpdGh1Yi5jb20vYW1jcmlzYW4vRXBpRGVzaWduUGF0dGVybikhCgpGaXJzdCwgd2UgbG9hZCB0aGUgbGlicmFyaWVzIGFuZCB0b29scyB0aGF0IHdlJ2xsIG5lZWQKCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KbGlicmFyeShhcGUpCmxpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ21hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZHlncmFwaHMpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KGxlYWZsZXQpCgpzb3VyY2UoInNlcnZlclV0aWxpdHkuUiIpICNjdXN0b20gYml0IG9mIGNvZGUgSSB3cm90ZSB3aXRoIGhlbHBlciBmdW5jdGlvbnMKc291cmNlKCJnbG9iYWwuUiIpICAjIHRoaXMgaXNuJ3QgbmVjZXNzYXJ5IGZvciBiYXNpYyBkYXRhIHZpeiwgYnV0IGl0J2xsIGNvbWUgaW4gaGFuZHkgaW4gdGhlIHNoaW55IGFwcApgYGAKCkxhc3RseSwgSSd2ZXNhdmVkIHRoZSBwcm9jZXNzZWQgbWV0YWRhdGEgYXMgYSBmaWxlLCB3aGljaCBJJ2xsIG5vdyBsb2FkIGZvciB0aGUgZGF0YSB2aXN1YWxpemF0aW9ucy4KCmBgYHtyIGxvYWRNZXRhfQptZXRhZGF0YTwtcmVhZFJEUygiLi9kYXRhL2Vib2xhX21ldGFkYXRhLlJEUyIpCmBgYAoKCmBgYHtyfQpgYGAKCiMjIyBQaHlsb2dlbmV0aWMgVHJlZQoKVG8gcGxvdCB0aGUgcGh5bG9nZW5ldGljIHRyZWUgd2UnbGwgdXNlIHRoZSBbYGFwZWBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9hcGUvaW5kZXguaHRtbCkgbGlicmFyeSB0byByZWFkIHRoZSB0cmVlIGludG8gUiBhbmQgW2BnZ3RyZWVgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvZ2d0cmVlLmh0bWwpIGxpYnJhcnkgdG8gYWRkIGFubm90YXRpb25zIHRvIHRyZWUuCgpgYGB7ciBwaHlsb1RyZWUsIGVjaG89RkFMU0UsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQppbnB1dDwtZGF0YS5mcmFtZSh0cmVlTGF5b3V0PSJyZWN0IiwKICAgICAgICAgICAgICAgICAgY29sb3JCeT0iQ291bnRyeSIsCiAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpgYGAKCmBgYHtyfQpteVRyZWUgPC0gcmVhZC5uZXh1cygiLi9hbmFseXNpcy9NYWtvbmEudHJlZSIpIAp0cmVlPC1nZ3RyZWUobXlUcmVlKQp0cmVlPC1jb2xvclRyZWVUaXAodHJlZSxtZXRhZGF0YSwiQ291bnRyeSIpCnRyZWUKYGBgCgpBcyBhbiBhc2lkZSwgdGhlIGNvbG9yVHJlZVRpcCBmdW5jdGlvbiBpcyBvbmUgdGhhdCBJIGhhdmUgY3JlYXRlZCwgYW5kIHRoYXQgcmVzaWRlcyBpbiB0aGUgPHR0PnNlcnZlclV0aWxpdHkuUjwvdHQ+IG1ldGhvZC4gVGhlIGNvZGUgZm9yIHRoYXQgbWV0aG9kIGlzIGFzIGZvbGxvd3M6CgpgYGB7cn0KCiNGVU5DVElPTiBDT0xPUiBUUkVFVElQRQpjb2xvclRyZWVUaXAgPSBmdW5jdGlvbih0cmVlLG1ldGFkYXRhLHZhcikgewogIHRyZWU8LXRyZWUgJTwrJSBtZXRhZGF0YSArIGdlb21fdGlwcG9pbnQoYWVzX3N0cmluZyhjb2xvcj12YXIpLHNpemU9NSwgYWxwaGE9MC4zNSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikKCiAgaWYodmFyICVpbiUgYygiQ291bnRyeSIpKXsKICAgICN0aGUgZHJvcD1GQUxTRSBpcyBuZWNlc3NhcnkgdG8gbWFpbnRhaW4gYSBjb25zaXN0ZW50IGNvbG91ciBzY2FsZSBhY3Jvc3Mgc2V2ZXJhbAogICAgI3Zpc3VhbGl6YXRpb25zLiBUaGlzIHdpbGwgYmUgaW1wb3J0YW50IGluIHRoZSBzaGlueSBhcHBsaWNhdGlvbgogICAgdHJlZTwtdHJlZSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YXMuY2hhcmFjdGVyKGNvdW50cnlDb2wkY29sVmFscyksZHJvcD1GQUxTRSkKICB9CiAgCiAgdHJlZSAjcmV0dXJuIHRoZSAKfQoKYGBgCgpUaGUgYWJvdmUgY29kZSBwcm92aWRlcyBhIGN1c3RvbSBjb2xvdXIgc2NhbGUgLCB3aGVuIHdlIGFyZSB0cnlpbmcgdG8gYW5ub3RhdGUgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggPHR0PkNvdW50cnk8L3R0Pi4gWW91IGNhbiBhbHNvIGNvbG91ciB0aGUgdHJlZSBieSA8dHQ+UmVnaW9uPC90dD4sIGJ1dCBpdCBpcyB2ZXJ5IGhhcmQgdG8gY2hvb3NlIHRoZSByaWdodCBjb2xvdXIgc2NoZW1lIGZvciBzbyBtYW55IGRpZmZlcmVudCByZWdpb25zLCBzbyB3ZSBsZXQgUiBqdXN0IHBpY2sgY29sb3VycyBvZiB1cy4KCmBgYHtyIHBoeWxvUmVnaW9ufQp0cmVlPC1nZ3RyZWUobXlUcmVlKQp0cmVlPC1jb2xvclRyZWVUaXAodHJlZSxtZXRhZGF0YSwiUmVnaW9uIikKdHJlZQpgYGAKCiMjIyBNYXAKCldlIHdpbGwgbm93IHVzZSB0aGUgW2BsZWFmbGV0YF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0LykgcGFja2FnZSB0byBwbG90IHRoZSBsb2NhdGlvbnMgb2YgdGhlIGRpZmZlcmVudCBjYXNlcy4gTmFpdmVseSwgd2UgY291bGQganVzdCBwbG90IGV2ZXJ5IHNpbmdsZSBkYXRhIHBvaW50LCBidXQgdGhpcyBpcyBhY3R1YWxseSBub3QgdmVyeSBoZWxwZnVsLiBGb3IgcHJpdmFjeSBjb25jZXJucywgYWxsIGRhdGEgcG9pbnRzIGFyZSBwbG90dGVkIHRvIHRoZSByZWdpb24gbGV2ZWwsIHNvIHdlJ2QgaGF2ZSBhIGJ1bmNoIG9mIGRhdGEgcG9pbnRzIHNpdHRpbmcgb24gdG9wIG9mIGVhY2ggb3RoZXIsIHdoaWNoIGlzIG5vdCBhIHZlcnkgdXNlZnVsIG1hcC4KCkluc3RlYWQsIHdlJ2xsIHVzZSB0aGUgW2BkcGx5cmBdKGh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbS93ZWIvcGFja2FnZXMvZHBseXIvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKSBwYWNrYWdlIHRvIDx0dD5jb3VudDwvdHQ+IHRoZSB0b3RhbCBudW1iZXIgb2YgY2FzZXMgd2l0aGluIGEgY291bnRyeSBvciByZWdpb24sIGFuZCBqdXN0IHBsb3QgdGhhdCBpbnN0ZWFkLiBXZSdsbCBtYWtlIHRoZSA8dHQ+c2l6ZTwvdHQ+IG9mIHBvaW50cyBvbiB0aGUgcGxvdCByb3VnaGx5IHByb3BvcnRpb25hbCB0byB0aGUgdG90YWwgbnVtYmVyIG9mIGNhc2VzLCB3ZSdsbCBhbHNvIGFkZCBzb21lIHBvcC11cCB0ZXh0IHRvIGluZGljYXRlIHRoZSBjYXNlIG51bWJlciEuCgpgYGB7cn0KIyBGaXJzdCwgd2UgbmVlZCB0byBjcmVhdGUgc29tZSBuZXcgZGF0YQojIENvdW50aW5nIG51bWJlciBjYXNlcyBwZXIgY291bnRyeQoKYWdnRGF0PC1tZXRhZGF0YSAlPiUKICAgICAgIGZpbHRlcihDb3VudHJ5ICE9Ij8iKSAlPiUKICAgICAgIGdyb3VwX2J5KENvdW50cnksY291bnRyeV9sb24sY291bnRyeV9sYXQpICU+JQogICAgICAgZHBseXI6OmNvdW50KCklPiUKICAgICAgIG11dGF0ZShwb3B1cD1zcHJpbnRmKCIlcyA9ICVkIGNhc2VzIixDb3VudHJ5LG4pKSAjY3JlYXRlIGEgcG9wdXAgZm9yIHRoZSBtYXAKCiMgSGVyZSdzIGEgdmVyeSBxdWljayBsb29rIGF0IHdoYXQgdGhpcyBjb21tYW5kIGdlbmVyYXRlcyBmb3IgdXM6CmFnZ0RhdAoKYGBgCgpOb3cgd2UnbGwgcGxvdCB0aGUgYWN0dWFsIG1hcCB1c2luZyB0aGUgZGF0YSB3ZSAqZGVydmllZCogKHRoZSBhZ2dEYXRhIGRhdGEuZnJhbWUpCgpgYGB7cn0KIyBOb3csIHdlJ2xsIGNyZWF0ZSB0aGUgTWFwCiMgVGhpcyBmaXJzdCBjb21tYW5kIHdpbGwgY3JlYXQgYW4gZW1wdHkgbWFwCm08LWxlYWZsZXQoYWdnRGF0KQoKICBtICU+JQogICAgICAgIGFkZFRpbGVzKCklPiUgCiAgICAgICAgYWRkQ2lyY2xlTWFya2VycygKICAgICAgICAgIGxuZz1+Y291bnRyeV9sb24sCiAgICAgICAgICBsYXQ9IH5jb3VudHJ5X2xhdCwKICAgICAgICAgIHJhZGl1cz1+c3FydChuKSoyLAogICAgICAgICAgY29sb3IgPSB+cGFsKENvdW50cnkpLAogICAgICAgICAgc3Ryb2tlID0gRkFMU0UsIGZpbGxPcGFjaXR5ID0gMC43LAogICAgICAgICAgbGFiZWw9fmFzLmNoYXJhY3Rlcihwb3B1cCksCiAgICAgICAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMobm9IaWRlID0gVCksCiAgICAgICAgICBvcHRpb25zID0gbGVhZmxldE9wdGlvbnMobWluWm9vbSA9IDAsIG1heFpvb20gPSAxMCxzY3Jvb2xXaGVlbFpvb209RkFMU0UpKQpgYGAKCkJ5IG1vZGlmeWluZyB0aGUgY29kZSB2ZXJ5IHNsaWdodGx5LCB3ZSBjYW4gYWxzbyBwbG90IGRhdGEgcmVnaW9uYWxseSBhcyBvcHBvc2VkIHRoZSBjb3VudHJ5IGxldmVsLgoKYGBge3J9CmFnZ0RhdDwtbWV0YWRhdGEgJT4lCiAgICAgICAgZmlsdGVyKENvdW50cnkgIT0iPyIpICU+JQogICAgICAgIGdyb3VwX2J5KENvdW50cnksUmVnaW9uLHJlZ2lvbl9sb24scmVnaW9uX2xhdCkgJT4lCiAgICAgICAgZHBseXI6OmNvdW50KCklPiUgCiAgICAgICAgbXV0YXRlKHBvcHVwPXNwcmludGYoIiVzICglcykgPSAlZCBjYXNlcyIsUmVnaW9uLENvdW50cnksbikpCiAgICAgIAptPC1sZWFmbGV0KGFnZ0RhdCkKICAgICAgCm0gJT4lCiAgYWRkVGlsZXMoKSU+JSAKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgbG5nPX5yZWdpb25fbG9uLAogICAgbGF0PSB+cmVnaW9uX2xhdCwKICAgIHJhZGl1cz1+c3FydChuKSoyLAogICAgY29sb3IgPSB+cGFsKENvdW50cnkpLCAjd2UgYWN0dWFsbHkgY29sb3VyIHRoZSBwb2ludHMgYnkgY291bnRyeSBoZXJlCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICBsYWJlbD1+YXMuY2hhcmFjdGVyKHBvcHVwKSwKICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyhub0hpZGUgPSBGKQogICkKYGBgCgpgTGVhZmxldGAgYWxzbyBhbGxvd3MgeW91IHRvIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGUgY2x1c3RlcnMgdGhhdCB3aWxsIHZhcnkgd2hlbiB5b3Ugem9vbSBpbiBhbmQgb3V0IG9uIHRoZSBtYXAuIFdlIGRpZG4ndCBpbXBsZW1lbnQgdGhpcyBmZWF0dXJlIGluIHRoZSBzaGlueSBhcHBsaWNhdGlvbiwgYnV0IEkgd2FudGVkIHRvIGxldCB5b3Uga25vdyB0aGlzIGlzIGF2YWlsYWJsZS4gCgpgYGB7cn0KbTwtbGVhZmxldChtZXRhZGF0YSkgCgojIGJ5IHByb3ZpZGluZyB0aGUgcmVnaW9uIGxhdGl0dWRlIGFuZCBsb25ndGl0dWRlIGNvLW9yZGluYXRlcyB3ZSBhbGxvdyBjbHVzdGVyaW5nIG9mIHJlZ2lvbmFsIHNhbXBsZXMKbSAlPiUKICBhZGRUaWxlcygpJT4lCiAgYWRkQ2lyY2xlTWFya2VycygKICAgIGxuZz1+cmVnaW9uX2xvbiwKICAgIGxhdD0gfnJlZ2lvbl9sYXQsCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsCiAgICBjbHVzdGVyT3B0aW9ucz0gbWFya2VyQ2x1c3Rlck9wdGlvbnModGl0aWxlPSJyZWdpb25hbCBjbHVzdGVycyIpICNjbHVzdGVyIG9wdGlvbnMKICApCgpgYGAKClRoZSBjbHVzdGVyIGNvbG91ciBoZXJlIGFyZSBhdXRvbWF0aWNhbGx5IGFzc2lnbmVkIGJ5IHRoZSBgTGVhZmxldGAgcGFja2FnZSwgYW5kIGFyZSBhcyBmb2xsb3dzOgoKKiBvcmFuZ2UgLyByZWQgZm9yIHRoZSBsYXJnZXN0IGNsdXN0ZXJzCiogeWVsbG93IGZvciAibWVkaXVtIiBjbHVzdGVycwoqIGdyZWVuIGZvciBzbWFsbCBjbHVzdGVycwoKQnkgY2xpY2sgb24gYSBwb2ludCwgdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHJldmVhbHMgdGhlIGdlb3NwYXRpYWwgbG9jYXRpb25zIHRoYXQgd2VyZSBhZ2dyZWdhdGVkIGludG8gYSBjbHVzdGVyICh3aGljaCBpdHMgYSBuZWF0IGFuZCBoYW5keSBmZWF0dXJlKS4gCgojIyMgVGltZWxpbmUgCjxhIGlkPSJ0aW1lbGluZVN0YXRpYyI+PC9hPgoKRmluYWxseSwgd2Ugd2lsbCB1c2UgdGhlIFtgZHlncmFwaHNgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2R5Z3JhcGhzLykgcGFja2FnZSB0byBjcmVhdGUgYSB0aW1lbGluZSBwbG90IHRoYXQgc2hvd3MgdGhlIGFnZ3JlZ2F0ZSBudW1iZXIgb2YgY2FzZXMgb3ZlciB0aW1lIGluIGEgY291bnRyeS4gIFRoZSBgZHlncmFwaHNgIHBhY2thZ2UgcmVxdWlyZXMgYW4gPHR0Pnh0czwvdHQ+IHRpbWVzZXJpZXMgb2JqZWN0IHRvIGNyZWF0ZSB0aGUgcGxvdHMsIHNvIHdlJ2xsIHVzZSB0aGUgW2B4dHNgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMveHRzL2luZGV4Lmh0bWwpIHBhY2thZ2UgaW4gYWRkaXRpb24gdG8gYGRwbHlyYCB0byBjcmVhdGUgdGhlIHRpbWUgc2VyaWVzLgoKRmlyc3QsIGxldCdzIHNldCB1cCB0aGUgdGltZXNlcmllcywgd2hpY2ggaXMgdGhlIGFnZ3JlZ2F0ZSAqbW9udGhseSogbnVtYmVyIG9mIGNhc2VzICpwZXIgY291bnRyeSouIFRoZSBtZXRhZGF0YSBoYXMgdGhlIHNhbXBsZSA8dHQ+Y29sbGVjdGlvbiBkYXRlPC90dD4gKHllYXItbW9udGgtZGF5KSwgYnV0IHRvIGdldCB0aGUgbW9udGhseSBhZ2dyZWdhdGUgc3VtIHdlICBuZWVkIHdyYW5nbGUgdGhlIGRhdGEgYSBsaXR0bGUgYml0IG1vcmUuCgpGaXJzdCwgbGV0J3MgbWFrZSB0aGUgdGltZXNlcmllcywgYnkgd3JhbmdsaW5nIHRoZSBkYXRhIGEgYml0OgoKYGBge3J9CiNjb3VudCBjYXNlcyBieSBkYXRlLCB3ZSdyZSBhbHNvIGdvaW5nIHRvIGFnZ3JlZ2F0Z2UgYnkgKm1vbnRoKiBzbyB3ZSdyZSBnb2luZyB0byAKICAgICNjcmVhdGUgYSBuZXcgdGltZSB2YXJpYWJsZQogICAgdGltZXNlcmllc0RhdGE8LW1ldGFkYXRhICU+JQogICAgICBtdXRhdGUoeWVhck1vbnRoPXltZChzYXBwbHkoWWVhck1vbnRoLGZ1bmN0aW9uKHgpe3Bhc3RlKHgsIjAxIixzZXA9Ii0iKX0pKSkgJT4lIAogICAgICBncm91cF9ieSh5ZWFyTW9udGgpJT4lIAogICAgICBkcGx5cjo6Y291bnQoQ291bnRyeSkgJT4lCiAgICAgIGNvbXBsZXRlKHllYXJNb250aCxDb3VudHJ5KSAlPiUgI21ha2Ugc3VyZSB0aGF0IGFsbCBkYXRlcyBhcmUgcmVwcmVzZW50ZWQKICAgICAgbXV0YXRlKG49cmVwbGFjZShuLGlzLm5hKG4pLDApKSAjdHVybiBOQXMgZnJvbSBhYm92ZSBjb21tYW5kIGluIHplcm9zCgpgYGAKCk5leHQsIGxldCdzIHRha2UgdGhhdCB0aW1lIHNlcmllcyBhbmQgdHVybiBpdCBpbnRvIGFuIDx0dD54dHM8L3R0PiBvYmplY3QKCmBgYHtyfQogICAgCiNjcmVhdGUgYW4geHRzIG9iamVjdAp4dHNPYmo8LWMoKQogIGZvcihpIGluIHVuaXF1ZSh0aW1lc2VyaWVzRGF0YSRDb3VudHJ5KSl7CiAgICB0ZW1wPC10aW1lc2VyaWVzRGF0YSAlPiUKICAgICAgZmlsdGVyKENvdW50cnkgPT0gaSkKICAgIAogICAgeHRzT2JqPC1jYmluZCh4dHNPYmoseHRzKHRlbXAkbiwgdGVtcCR5ZWFyTW9udGgpKQogIH0KICAgIAojbmFtZSBvdXQgb2JqZWN0LCBzbyB0aGF0IGl0IHBsb3RzIHRoZSB0aW1lIHNlcmllcyBjb3JyZWN0bHkKY29sbmFtZXMoeHRzT2JqKTwtdW5pcXVlKHRpbWVzZXJpZXNEYXRhJENvdW50cnkpCmBgYAoKQXMgZmluYWwgc3RlcCwgd2UgY2FuIG5vdyBjcmVhdGUgdGhlIDx0dD5keWdyYXBoPC90dD4uIFRoaXMgZ3JhcGggaXMgaW50ZXJhY3RpdmU7IHlvdSBjYW4gaW50ZXJhY3Qgd2l0aCB0aGUgPHR0PmR5Z3JhcGg8L3R0PiBieSBjaGFuZ2UgdGhlIHJhbmdlIG9uIHRoZSBzbGlkZXIgYmVsb3csIG9yIGJ5IGhpZ2hsaWdodGluZyBhIHJlZ2lvbiBvZiB0aGUgZHlyZ3JhcGggd2l0aCB5b3VyIG1vdXNlLgoKYGBge3J9CiNub3cgbWFrZSB0aGUgdGhlIGR5Z3JhcGggKHlheSEpCmR5Z3JhcGgoeHRzT2JqKSAlPiUgCiAgZHlPcHRpb25zKHN0YWNrZWRHcmFwaCA9IFRSVUUsY29sb3JzID0gY291bnRyeUNvbCRjb2xWYWxzKSAlPiUKICBkeVJhbmdlU2VsZWN0b3IoZmlsbENvbG9yPSIjYzk3ZjkxIixzdHJva2VDb2xvcj0iI2M5N2Y5MSIpCmBgYAoKCiMgUHV0dGluZyB0aGluZ3MgdG9nZXRoZXIgaW4gdGhlIHNoaW55IGFwcGxpY2F0aW9uCgpUaGUgYWJvdmUgY29kZSBjb3VsZCBiZSB1c2VkIHRvIGNyZWF0ZSBhIHNoaW55IGFwcGxpY2F0aW9uLCBidXQgYWxsIHlvdSdkIGhhdmUgYXJlIHRocmVlIGltYWdlcyB0aGF0IGRvbid0IHJlYWxseSBpbnRlcmFjdCB3aXRoIGVhY2ggb3RoZXIuIEluIHRoaXMgcGFydCwgSSdsbCBzaG93IHlvdSBob3cgeW91IGNhbiBjcmVhdGUgbGlua3MgYmV0d2VlbiB0aGUgdmlzdWFsaXphdGlvbnMgdG8gYWxsb3cgZm9yICpjby1vcmRpbmF0ZWQqIGludGVyYWN0aW9ucy4KClRoZSBmdWxsIHNoaW55IGFwcGxpY2F0aW9uIHdlJ3JlIHdvcmtpbmcgdG93YXJkcyBjYW4gYmUgdGVzdGVkIG91dCBoZXJlOiBgW2h0dHBzOi8vYW1jcmlzYW4uc2hpbnlhcHBzLmlvL0VwaURlc2lnblBhdHRlcm4vXShodHRwczovL2FtY3Jpc2FuLnNoaW55YXBwcy5pby9FcGlEZXNpZ25QYXR0ZXJuLylgCgpUaGUgbWFpbiBpbnRlcmFjdGlvbiBkcml2ZXIgaW4gdGhlIGFwcGxpY2F0aW9uIGlzIDx0dD5keWdyYXBoPC90dD4gdGltZWxpbmUuIFlvdSBjYW4gaGlnaGxpZ2h0IGEgcmVnaW9uIG9mIHRoZSB0aW1lbGluZSBhbmQgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlICYgbWFwIHdpdGggY2hhbmdlIGFjY29yZGluZ2x5LiAKCiMjIFdvcmtpbmcgd2l0aCB1aS5SIGFuZCBzZXJ2ZXIuUgoKRWFybGllciBpbiB0aGlzIGRvY3VtZW50LCB3ZSBpbnRyb2R1Y2VkIHRoZSB0aHJlZSBtYWluIGZpbGVzIHNoaW55IGZpbGVzOiBgdWkuUmAsIGBzZXJ2ZXIuUmAsIGFuZCBgZ2xvYmFsLlJgLiBIZXJlIEknbGwgc2hvdyBob3csIGZvciBvdXIgZXhhbXBsZSwgd2Ugd291bGQgdXNlIGB1aS5SYCBhbmQgYHNlcnZlci5SYCBpbiBwYXJ0aWN1bGFyLiBJJ20gdXNpbmcgYSBwYWNrYWdlIGNhbGxlZCBbYHNoaW55ZGFzaGJvYXJkYF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9zaGlueWRhc2hib2FyZC8pIGZvciB0aGUgdmlzdWFsIGFwcGVhcmFuY2Ugb2YgbXkgc2hpbnkgYXBwbGljYXRpb24sIHdoaWNoIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgZGVmYXVsdCBgdWkuUmAgY29kZSwgYnV0IHdpdGggc29tZSBzbGlnaHQgZGlmZmVyZW5jZXMuCgpPayAtIHNvLCBsZXQncyBqdXN0IGltYWdpbmUgYSB2ZXJ5IHNpbXBsZSBzaGlueSBhcHAgOiBpdCBqdXN0IHNob3dzIGEgcGh5bG9nZW5ldGljIHRyZWUgYW5kIGEgdXNlciBjYW4gY2hvb3NlIHRvIGFubm90YXRlIHRoZSB0aXBzIGFjY29yZGluZyB0byB0byA8dHQ+Y291bnRyeTwvdHQ+IG9yIDx0dD5yZWdpb248L3R0Pi4gSGVyZSdzIHdoYXQgdGhpcyBsaXR0bGUgYml0IG9mIHNoaW55IGNvZGUgd291bGQgbG9vayBsaWtlLgoKCjx0dD4qKnVpLlIqKjwvdHQ+CgpgYGB7ciAsIGV2YWw9RkFMU0V9CgpkYXNoYm9hcmRQYWdlKHNraW4gPSAiYmxhY2siLAogIGRhc2hib2FyZEhlYWRlcigpLAogIGRhc2hib2FyZFNpZGViYXIoCiAgICBoMigiVHJlZSBPcHRpb25zIiksCiAgICBzZWxlY3RpemVJbnB1dChpbnB1dElkPSJjb2xvckJ5IiwKICAgICAgICAgICAgICAgICAgIGxhYmVsPSJDb2xvciBCeSIsCiAgICAgICAgICAgICAgICAgICBjaG9pY2VzPWMoIkRhdGUiLCJDb3VudHJ5IiwiUmVnaW9uIiksCiAgICAgICAgICAgICAgICAgICBtdWx0aXBsZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkPSJDb3VudHJ5IikKICApLAogIGRhc2hib2FyZEJvZHkoCiAgICB0YWdzJGhlYWQoCiAgICAgIHRhZ3MkbGluayhyZWwgPSAic3R5bGVzaGVldCIsIHR5cGUgPSAidGV4dC9jc3MiLCBocmVmID0gImN1c3RvbS5jc3MiKQogICAgKSwKICAgIHBsb3RPdXRwdXQoInRyZWVQbG90IikKICApCikKCmBgYAoKSW4gdGhlIGFib3ZlIGNvZGUgPHR0PnNlbGVjdGl6ZUlucHV0PC90dD4gY3JlYXRlcyBhIGRyb3Bkb3duIGxpc3QsIHdoaWNoIGluZGljYXRlcyB3aGV0aGVyIHRoZSB0cmVlIHNob3VsZCBiZSBjb2xvdXJlZCBieSBjb3VudHJ5LCByZWdpb24sIG9yIGRhdGUuIEl0IHdpbGwgc3RvcmUgdGhpcyBpbmZvcm1hdGlvbiBpbiBhIHZhcmlhYmxlIGNhbGxlZCA8dHQ+Y29sb3JCeTwvdHQ+LiBUaGUgcGh5bG9nZW5ldGljIHRyZWUgd2lsbCBiZSBwbG90dGVkIGJ5IHRoZSA8dHQ+cGxvdE91dHB1dDwvdHQ+IGZ1bmN0aW9uICAodGhpcyB3b3JrcyBoZXJlIGJlY2F1c2UgPHR0PmdndHJlZTwvdHQ+LCB3aGljaCBjcmVhdGUgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlLCBpcyBhIDx0dD5nZ3Bsb3Q8L3R0PiBkYXRhIHR5cGUpLCBhbmQgd2lsbCB0aGUgY29sb3VyZWQgYWNjb3JkaW5nIHRvIHdoYXRldmVyIHZhcmlhYmxlIGlzIGluZGljYXRlZCBpbiBieSB0aGUgPHR0PmNvbG9yQnk8L3R0PiB2YXJpYWJsZS4gCgpJbXBvcnRhbnRseTogPHR0PnNlbGVjdGl2ZUlucHV0PC90dD4gdGFrZXMgZGF0YSBmcm9tIHRoZSB1c2VyIHZpYSBhIGRyb3AgZG93biBsaXN0IGFuZCBwcm92aWRlcyB0aGVzZSBkYXRhIHRvIDx0dD5zZXJ2ZXIuUjwvdHQ+IGFzIGFuIDx0dD5pbnB1dCB2YXJpYWJsZTwvdHQ+LiBDb252ZXJzZWx5LCA8dHQ+cGxvdE91dHB1dDwvdHQ+IHRha2VzIGEgcmVzdWx0aW5nIGdyYXBoLCB3aGljaCBpcyBwcm9kdWNlZCBpbiA8dHQ+c2VydmVyLlI8L3R0PiwgaGVuY2UgaXQgaXMgYW4gPHR0Pm91cHV0IHZhcmlhYmxlPC90dD4uCgpIZXJlJ3Mgd2hhdCB0aGUgc2VydmVyIGNvZGUgd291bGQgbG9vayBsaWtlIHRvIGJyaW5nIHRoaXMgcGxvdCB0byBsaWZlOgoKCjx0dD4qKnNlcnZlci5SKio8L3R0PgoKYGBge3IgLCBldmFsPUZBTFNFfQpzaGlueVNlcnZlcihmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CgogICN3ZSBzdG9yZSB0cmVlUGxvdCBhcyBhbiBvdXRwdXQgdmFyaWFibGUKICBvdXRwdXQkdHJlZVBsb3QgPC0gcmVuZGVyUGxvdCh7CiAgICAgIAogICAgICAjIFdlJ3JlIGdvaW5nIHRvIGxvYWQgdHJlZXMgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBzdG9yZWQuCiAgICAgICMgVGhlIGFsdGVybmF0aXZlIGlzIHRvIHJlLWNvbXB1dGUgdHJlZSBlYWNoIHRpbWUsIHdoaWNoIGNhbiBiZSBzbG93CiAgICAgICMgU2luY2Ugd2UncmUga2VlcGluZyB0aGUgYmFzZSBzdHJ1Y3R1cmUsIGl0J3MgZ29vZCB0byAKICAgICAKICAgICAgdHJlZTwtcmVhZFJEUygiLi9kYXRhL2Vib2xhVHJlZS5SRFMiKSAgIyBkZWZhdWx0IGlzIHJvb3RlZCB0cmVlCiAgCiAgICAgICN3ZSdsbCBnZXQgdGhlIHZhbHVlIGZyb20gdGhlIGNvbG9yQnkgdmFyaWFibGUgdG8gZGVjaWRlZAogICAgICAjaG93IHRvIHBsb3QgdGhlIHRyZWUgdGlwLiAKICAgICAgdHJlZTwtY29sb3JUcmVlVGlwKHRyZWUsbWV0YWRhdGEsaW5wdXQkY29sb3JCeSkKICAgICAgI3JldHVybiB0aGUgdHJlZQogICAgICB0cmVlCiAgfSkKfSkKYGBgCgpTbywgaG93IGRvZXMgdGhpcyBhbGwgd29yayB0b2dldGhlcj8gV2VsbCwgdGhlIDx0dD50cmVlUGxvdDwvdHQ+IHdpbGwgZ2VuZXJhdGUgd2hlbiB0aGUgc2hpbnkgYXBwbGljYXRpb24gaXMgc3RhcnRlZCBhbmQgd2lsbCBjb2xvdXIgaXRzIHRpcHMgYnkgdXNpbmcgdGhlIGRlZmF1bHQgPHR0PmNvbG9yQnk8L3R0PiB2YWx1ZSAoQ291bnRyeSkuIFdoZW4gdGhlIHVzZXIgcGlja3MgYSAqZGlmZmVyZW50KiB2YWx1ZSBmcm9tIDx0dD5zZWxlY3RpemVJbnB1dDwvdHQ+LCBzaGlueSB3aXRoICphdXRvbWF0aWNhbGx5KiByZWRyYXcgdGhlIHRyZWUgYW5ub3RhdGlvbnMuIAoKCiMjIFJlYWN0aXZlIGRhdGEKCkZvciBtb3JlIGNvbXBsZXggYXBwbGljYXRpb25zIHRoYXQgaGF2ZSBtb3JlIG1vdmluZyBwYXJ0cyB0aGF0IG5lZWQgdG8gY28tb3JkaW5hdGUsIHdlIG5lZWQgYSBtb3JlIGNvbXBsaWNhdGVkIGRhdGEgdHlwZSA6IGEgPHR0PltyZWFjdGl2ZSBkYXRhIG9iamVjdHNdKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vdHV0b3JpYWwvbGVzc29uNi8pPC90dD4KCkZvciB0aGlzIGFwcGxpY2F0aW9uLCBJJ3ZlIG1hZGUgYSBkZWNpc2lvbjogYWxsIHRoZSBpbnRlcmFjdGlvbnMgd2lsbCBiZSBiYXNlZCBvbiB0aGUgdGltZWxpbmUgZ3JhcGguIFNvLCBJIHdhbnQgYSBkYXRhc2V0IHRoYXQgY2hhbmdlcyBhY2NvcmRpbmcgdG8gdGhlIGRhdGUgcmFuZ2UuIFRoaXMgcmVhY3RpdmUgZGF0YXNldCB3b3VsZCBsaXZlIGluc2lkZSA8dHQ+c2VydmVyLlI8L3R0Pi4gU28sIEknbGwgYWRkIHRoZSB0aW1lc2VyaWVzIHRvIDx0dD51aS5SPC90dD4sIGFuZCBJJ2xsIHdlJ2xsIHNlZSBob3cgdGhlIDx0dD5zZXJ2ZXIuUjwvdHQ+IGNvZGUgd2lsbCBjaGFuZ2UuCgoKPHR0PioqdWkuUioqPC90dD4KCmBgYHtyICwgZXZhbD1GQUxTRX0KCmRhc2hib2FyZFBhZ2Uoc2tpbiA9ICJibGFjayIsCiAgZGFzaGJvYXJkSGVhZGVyKCksCiAgZGFzaGJvYXJkU2lkZWJhcigKICAgIGgyKCJUcmVlIE9wdGlvbnMiKSwKICAgIHNlbGVjdGl6ZUlucHV0KGlucHV0SWQ9ImNvbG9yQnkiLAogICAgICAgICAgICAgICAgICAgbGFiZWw9IkNvbG9yIEJ5IiwKICAgICAgICAgICAgICAgICAgIGNob2ljZXM9YygiRGF0ZSIsIkNvdW50cnkiLCJSZWdpb24iKSwKICAgICAgICAgICAgICAgICAgIG11bHRpcGxlPUZBTFNFLAogICAgICAgICAgICAgICAgICAgc2VsZWN0ZWQ9IkNvdW50cnkiKQogICksCiAgZGFzaGJvYXJkQm9keSgKICAgIHRhZ3MkaGVhZCgKICAgICAgdGFncyRsaW5rKHJlbCA9ICJzdHlsZXNoZWV0IiwgdHlwZSA9ICJ0ZXh0L2NzcyIsIGhyZWYgPSAiY3VzdG9tLmNzcyIpCiAgICApLAogICAgcGxvdE91dHB1dCgidHJlZVBsb3QiKSwgI2hlcmUncyBvdXIgcGh5bG9nZW5ldGljIHRyZWUKICAgIGR5Z3JhcGhPdXRwdXQoInRpbWVsaW5lIikgI2hlcmUncyBvdXIgdGltZWxpbmUsIGl0IGFmZmVjdCB0aGUgbWV0YWRhdGFSZWFjdGl2ZSB2YXJpYWJsZQogICkKKQpgYGAKIAogQW5kIG5vdyB0aGUgc2VydmVyIGNvZGU6CiAKCjx0dD4qKnNlcnZlci5SKio8L3R0PgoKYGBge3IgLCBldmFsPUZBTFNFfQpzaGlueVNlcnZlcihmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CgogICMgbWV0YWRhdGEgdmFyaWFibGUgdGhhdCBjaGFuZ2VzIHJlYWN0aXZlIGFjY29yZGluZyB0byB0aGUKICAjIHRpbWVsaW5lIGRhdGUgcmFuZ2UKICBtZXRhZGF0YVJlYWN0aXZlIDwtIHJlYWN0aXZlKHsKICAgIHN0YXJ0RGF0ZTwtaW5wdXQkdGltZWxpbmVfZGF0ZV93aW5kb3dbWzFdXQogICAgZW5kRGF0ZTwtaW5wdXQkdGltZWxpbmVfZGF0ZV93aW5kb3dbWzJdXQogICAgCiAgICBpZihpcy5udWxsKHN0YXJ0RGF0ZSkpewogICAgICBtZXRhZGF0YSAjd2hlbiBwcm9ncmFtIGluaXRpYWxpemVzLCBzdGFydCBkYXRlIHdpbGwgYmUgbnVsbCwgc28gd2Ugc2VuZCB1cCB0aGUgZnVsbCBkYXRhc2V0CiAgICB9ZWxzZXsKICAgICAgbWV0YWRhdGEgJT4lIGZpbHRlcihEYXRlPj1zdGFydERhdGUgJiBEYXRlIDw9IGVuZERhdGUpCiAgICB9CiAgfSkKICAjLgogICMuCiAgIy4KICAjbm90IGluY2x1ZGVkOiBwaHlsb2dlbmV0aWMgdHJlZSBjb2RlLCB0aW1lbGluZSBkeWdyYXBoIGNvZGUKCn0pCmBgYAoKCk9rIC0gc28gd2UgZ2V0IHNvbWUgaW5mb3JtYXRpb24gZnJvbSB0aGUgZHlncmFwaCBhYm91dCB0aGUgZGF0ZSByYW5nZSAoPHR0PmlucHV0XCR0aW1lbGluZV9kYXRlX3dpbmRvd1tbMV1dPC90dD4gYW5kIDx0dD5pbnB1dFwkdGltZWxpbmVfZGF0ZV93aW5kb3dbWzJdXTwvdHQ+KS4gSW4gdGhpcyBleGFtcGxlLCB0aGUgPHR0PmR5Z3JhcGg8L3R0PiAoYW5kIG91dHB1dCB2YXJpYWJsZSBjYWxsZWQgPHR0PnRpbWVsaW5lPC90dD4pIGNhbiBhbHNvIHByb3ZpZGUgKmlucHV0cyogKGRlbm90ZWQgYnkgPHR0PltfZGF0ZV93aW5kb3ddKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vZHlncmFwaHMvc2hpbnkuaHRtbCk8L3R0PikgdG8gb3RoZXIgb3V0cHV0IHZhcmlhYmxlcy4gV2hlbiB5b3UgaW50ZXJhY3Qgd2l0aCB0aGUgdGltZWxpbmUgdmlzdWFsaXphdGlvbiAodGhlIGR5Z3JhcGgpLCBzaGlueSB3aWxsIHF1aWV0bHkgY29sbGVjdCA8dHQ+X2RhdGVfd2luZG93PC90dD4gZGF0YSBhbmQgcGFzcyB0aGlzIGluZm9ybWF0aW9uIHRvIG91ciByZWFjdGl2ZSBkYXRhIHR5cGUsIDx0dD5tZXRhZGF0YVJlYWN0aXZlPC90dD4uCgojIyBNYWtpbmcgb3VyIGRhdGEgdmlzdWFsaXphdGlvbnMgcmVhY3RpdmUKCk9LISBTbyBlYXJsaWVyIHdlIGNyZWF0ZWQgdGhlICpzdGF0aWMqIHZlcnNpb25zIG9mIG91ciBkYXRhIHZpc3VhbGl6YXRpb25zLCBub3cgd2UnbGwgY3JlYXRlIHRoZSAqaW50ZXJhY3RpdmUgdmVyc2lvbnMqIG9mIG91ciBkYXRhIHZpc3VhbGl6YXRpb25zLiBJbiB0aGUgYXJlYSBiZWxvdywgSSd2ZSBwcm92aWRlZCB0aGUgY29kZSB0aGF0IG5lZWRzIHRvIGdvIGludG8gdGhlIGBzZXJ2ZXIuUmAgZmlsZSwgYnV0IEkgd2lsbCBhbHNvIGluZGljYXRlIHRoZSBjb21wbGVtZW50YXJ5IGNvbW1hbmQgdGhhdCBzaG91bGQgYmUgaW4gYHVpLlJgIHRvIG91dHB1dCB0aGUgdmlzdWFsaXphdGlvbi4KCiMjIyBQaHlsb2dlbmV0aWMgVHJlZQoKRm9yIHRoZSBwaHlsb2dlbmV0aWMgdHJlZSwgd2Ugb25seSB3YW50IHRvIGFubm90YXRlIGl0ZW1zIGluIHRoZSBwaHlsb2dlbmV0aWMgdHJlZSBiYXNlZCB1cG9uIHRoZSBjYXNlcyB0aGF0IGZhbGwgaW50byB0aGUgZGF0ZSByYW5nZSB3ZSd2ZSBzZWxlY3RlZCBpbiB0aGUgdGltZWxpbmUgcGxvdCBhbmQgY29sb3VyIHRoZSB0cmVlIHRpcHMgYmFzZWQgdXBvbiB0aGUgPHR0PmNvbG9yQnk8L3R0PiB2YXJpYWJsZS4gVG8gb3V0cHV0IHRoaXMgcGh5bG9nZW5ldGljIHRyZWUgd2UgdXNlIHRoZSA8dHQ+cGxvdE91dHB1dDwvdHQ+IGZ1bmN0aW9uIGluIGB1aS5SYC4gV2UgYWxzbyBuZWVkIHRvIGFkZCBzb21lIGV4dHJhIHN0ZXBzIHRvIGdldCBvdXIgdHJlZSBjb2RlIHRvIHdvcmsgY29ycmVjdGx5IHdpdGggdGhlIHJlYWN0aXZlIG1ldGFkYXRhICg8dHQ+bWV0YWRhdGFSZWFjdGl2ZTwvdHQ+KS4gVGhpcyB3aGF0IGl0IHdvdWxkIGxvb2sgbGlrZToKCmBgYHtyICwgZXZhbD1GQUxTRX0Kb3V0cHV0JHRyZWVQbG90IDwtIHJlbmRlclBsb3QoewogICAgCiAgICAjIFdlJ3JlIGdvaW5nIHRvIGxvYWQgdHJlZXMgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBzdG9yZWQuCiAgICAjIFRoZSBhbHRlcm5hdGl2ZSBpcyB0byByZS1jb21wdXRlIHRyZWUgZWFjaCB0aW1lLCB3aGljaCBjYW4gYmUgc2xvdwogICAgIyBTaW5jZSB3ZSdyZSBrZWVwaW5nIHRoZSBiYXNlIHN0cnVjdHVyZSwgaXQncyBnb29kIHRvIAogICAgdHJlZTwtcmVhZFJEUygiLi9kYXRhL2Vib2xhVHJlZS5SRFMiKSAgIyBkZWZhdWx0IGlzIHJvb3RlZCB0cmVlCiAgICAKICAgICNtZXRhZGF0YSBpcyBhdmFpbGFibGUgYXMgYSBnbG9iYWwgdmFyaWFibGUsIHNvIHdlIGRvbid0IG5lZWQgdG8gbG9hZCBpdAogICAgI2J1dCB3ZSd2ZSBhbHNvIGNyZWF0ZWQgdGhpcyByZWFjdGl2ZSB2YXJpYWJsZSwgc28gd2UncmUgZ29pbmcgdG8gCiAgICAjYWxzbyBjb2xvdXIgYnkuCiAgICAjIFdlIGRvbid0IG5lZWQgYWxsIHRoZSBkYXRhLCBqdXN0IHNvbWUKICAgIGNvbFRyZWVNZXRhPC1tZXRhZGF0YVssYygiSUQiLCJDb3VudHJ5IiwiUmVnaW9uIiwiRGF0ZSIpXSAKICAgIGNvbFRyZWVNZXRhJENvdW50cnk8LWZhY3Rvcihjb2xUcmVlTWV0YSRDb3VudHJ5LGxldmVscz1jKGxldmVscyhjb2xUcmVlTWV0YSRDb3VudHJ5KSwiIikpICN0aGlzIGlzIGEgaGFjayB0byBhdm9pZCBhIGdndHJlZSBlcnJvcgogICAgCiAgICAjIHRvIGFjY2VzcyB0aGUgcmVhY3RpdmUgZGF0YSwgd2UgbmVlZCB0byBjYWxsIGl0IGFzIGZ1bmN0aW9uCiAgICB0ZW1wPC1tZXRhZGF0YVJlYWN0aXZlKCkgI3RoaXMgaXMgbm93IGEgZGF0YS5mcmFtZQogICAgCiAgICAjdGhpcyBpcyBhIHdheSBJJ3ZlIGNob29zZW4gdG8gImhpZGUiIHRyZWUgdGlwcyB0aGF0IGFyZSBub3QgcHJlc2VudAogICAgI2luIHRoZSBkYXRlIHJhbmdlIHNwZWNpZmljZWQgYnkgdGhlIHRpbWUgc2VyaWVzIHBsb3QuIAogICAgY29sVHJlZU1ldGE8LWNvbFRyZWVNZXRhICU+JQogICAgICBtdXRhdGUoQ291bnRyeSA9IHJlcGxhY2UoQ291bnRyeSwhKElEICVpbiUgdGVtcCRJRCksIiIpKQogICAgCiAgICAjaW5zdGVhZCBvZiBtZXRhZGF0YSwgSSB3aWxsIHBhc3MgY29sVHJlZU1ldGEgaW5zdGVhZCBvZiBtZXRhZGF0YQogICAgdHJlZTwtY29sb3JUcmVlVGlwKHRyZWUsY29sVHJlZU1ldGEsaW5wdXQkY29sb3JCeSkKICAKICAgIAogICAgI3JldHVybiB0aGUgdHJlZQogICAgdHJlZQogIH0pCmBgYAoKQXMgeW91IGNhbiBzZWUsIHRoZSBjb2RlIHRvIG1ha2UgYSB0cmVlIGludGVyYWN0aXZlIGFuZCBsaXN0ZW5pbmcgZm9yIGludGVyYWN0aW9ucyB3aXRoIG90aGVyIHRyZWVzIGlzIG1vcmUgY29tcGxpY2F0ZWQgdGhhdCBzdGF0aWMgdHJlZXMgYW5kIHJlcXVpcmVzIGEgbGl0dGxlIGV4dHJhIGRhdGEgd3JhbmdsaW5nLiAgRXZlcnkgdGltZSA8dHQ+bWV0YWRhdGFSZWFjdGl2ZTwvdHQ+IGlzIGNoYW5nZWQgKGR1ZSB0byBpbnRlcmFjdGlvbnMgd2l0aCB0aGUgdGltZXNlcmllcykgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIGlzIGF1dG9tYXRpY2FsbHkgcmVnZW5lcmF0ZWQgdGhhbmtzIHRvIFNoaW55J3MgaW50ZXJuYWwgbG9naWMgdGhhdCBhdXRvbWF0aWNhbGx5IGRldGVjdHMgc3VjaCBkZXBlbmRlbmNpZXMuIAoKIyMjIE1hcAoKV2UncmUgb25seSBnb2luZyB0byBzaG93IG1hcCBwb2ludHMsIGFuZCBhZ2dyZWdhdGVkIHN1bXMsIGJhc2VkIHVwb24gdGhlIGNhc2VzIHRoYXQgb2NjdXJyZWQgd2l0aG91dCBhIHNwZWNpZmllZCB0aW1lIHBlcmlvZC4gV2UgYWxzbyB3YW50IHRvIGNoYW5nZSB3aGV0aGVyIGRhdGEgYXJlIGFnZ3JlZ2F0ZWQgYXQgdGhlIENvdW50cnkgbGV2ZWwsIG9yIHRoZSByZWdpb24gbGV2ZWwgKGFmZmVjdGVkIGJ5IHRoZSA8dHQ+Y29sb3JCeTwvdHQ+IHZhcmlhYmxlKS4gVG8gb3V0cHV0IHRoaXMgbWFwIHdlIHVzZSB0aGUgPHR0PmxlYWZsZXRPdXRwdXQ8L3R0PiBmdW5jdGlvbiBpbiBgdWkuUmAuICBIZXJlJ3MgaG93IHRoZSBpbnRlcmFjdGl2ZSBtYXAgd291bGQgYmUgY29kZWQgdXAuIAoKYGBge3IgZXZhbD1GQUxTRX0KCiBvdXRwdXQkY2FzZU1hcDwtcmVuZGVyTGVhZmxldCh7CiAgICAgbTwtTlVMTAogICAgCiAgICBpZihpbnB1dCRjb2xvckJ5PT0iQ291bnRyeSIgfCBpbnB1dCRjb2xvckJ5PT0iRGF0ZSIpewoKICAgICAgYWdnRGF0PC1tZXRhZGF0YVJlYWN0aXZlKCkgJT4lCiAgICAgICAgZmlsdGVyKENvdW50cnkgIT0iPyIpICU+JQogICAgICAgIGdyb3VwX2J5KENvdW50cnksY291bnRyeV9sb24sY291bnRyeV9sYXQpICU+JQogICAgICAgIGRwbHlyOjpjb3VudCgpJT4lIAogICAgICAgIG11dGF0ZShwb3B1cD1zcHJpbnRmKCIlcyA9ICVkIGNhc2VzIixDb3VudHJ5LG4pKQogICAgICAKICAgICAgbTwtbGVhZmxldChhZ2dEYXQpIAogICAgICAKICAgICAgbSAlPiUKICAgICAgICBhZGRUaWxlcygpJT4lIAogICAgICAgIGFkZENpcmNsZU1hcmtlcnMoCiAgICAgICAgICBsbmc9fmNvdW50cnlfbG9uLAogICAgICAgICAgbGF0PSB+Y291bnRyeV9sYXQsCiAgICAgICAgICByYWRpdXM9fnNxcnQobikqMiwKICAgICAgICAgIGNvbG9yID0gfnBhbChDb3VudHJ5KSwKICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCBmaWxsT3BhY2l0eSA9IDAuNywKICAgICAgICAgIGxhYmVsPX5hcy5jaGFyYWN0ZXIocG9wdXApLAogICAgICAgICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKG5vSGlkZSA9IFQpCiAgICAgICAgKQogICAgfWVsc2UgaWYoaW5wdXQkY29sb3JCeT09IlJlZ2lvbiIpewoKICAgICAgYWdnRGF0PC1tZXRhZGF0YVJlYWN0aXZlKCkgJT4lCiAgICAgICAgZmlsdGVyKENvdW50cnkgIT0iPyIpICU+JQogICAgICAgIGdyb3VwX2J5KENvdW50cnksUmVnaW9uLHJlZ2lvbl9sb24scmVnaW9uX2xhdCkgJT4lCiAgICAgICAgZHBseXI6OmNvdW50KCklPiUgCiAgICAgICAgbXV0YXRlKHBvcHVwPXNwcmludGYoIiVzICglcykgPSAlZCBjYXNlcyIsUmVnaW9uLENvdW50cnksbikpCiAgICAgIAogICAgICBtPC1sZWFmbGV0KGFnZ0RhdCkKICAgICAgCiAgICAgIG0gJT4lCiAgICAgICAgYWRkVGlsZXMoKSU+JSAKICAgICAgICBhZGRDaXJjbGVNYXJrZXJzKAogICAgICAgICAgbG5nPX5yZWdpb25fbG9uLAogICAgICAgICAgbGF0PSB+cmVnaW9uX2xhdCwKICAgICAgICAgIHJhZGl1cz1+c3FydChuKSoyLAogICAgICAgICAgY29sb3IgPSB+cGFsKENvdW50cnkpLAogICAgICAgICAgc3Ryb2tlID0gRkFMU0UsIGZpbGxPcGFjaXR5ID0gMC43LAogICAgICAgICAgbGFiZWw9fmFzLmNoYXJhY3Rlcihwb3B1cCksCiAgICAgICAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMobm9IaWRlID0gRikKICAgICAgICApCiAgICB9CiAgICAKICB9KQoKYGBgCgpUaGUgbWFwIHBsb3R0aW5nIGNvZGUgaXMgaWRlbnRpY2FsIHRvIHRoZSBzdGF0aWMgdmVyc2lvbiwgKmV4Y2VwdCogaXQgdXNlcyA8dHQ+bWV0YWRhdGFSZWFjdGl2ZSgpPC90dD4gaW5zdGVhZCBvZiA8dHQ+bWV0YWRhdGE8L3R0Pi4gRXZlcnkgdGltZSA8dHQ+bWV0YWRhdGFSZWFjdGl2ZTwvdHQ+IGNoYW5nZXMsIHRoZSBtYXAgd2lsbCAqYXV0b21hdGljYWxseSogYmUgdXBkYXRlZC4KCiMjIyBUaW1lbGluZQoKVGhlIGNvZGUgZm9yIHRoZSB0aW1lbGluZSB3aWxsIGFjdHVhbGx5IHN0YXkgZXhhY3RseSB0aGF0IHNhbWUhIFRoZSA8dHQ+dWkuUjwvdHQ+IGVsZW1lbnQgdGhhdCB3aWxsIG91dHB1dCB0aGUgPHR0PmR5Z3JhcGg8L3R0PiB0aW1lc2VyaWVzIGlzIDx0dD5keWdyYXBoT3V0cHV0PC90dD4uIFRoZSBkYXRhc2V0IGZvciB0aGUgdGltZXNlcmllcyBpcyBjb25zdGFudCAoZG9lc24ndCBjaGFuZ2UpLCBzbyBpdHMgZXhhY3RseSB0aGUgc2FtZSBjb2RlIHRoYXQgd2UgdXNlIGZvciB0aGUgW3N0YXRpYyB2ZXJzaW9uXSgjdGltZWxpbmVTdGF0aWMpLgoKCiMjIEZpbmFsIFNoaW55IEFwcGxpY2F0aW9uIENvZGUKClRoYXQncyBpdCEgVGhlIGZ1bGwgY29kZSBmb3IgdGhlIGBzZXJ2ZXIuUmAgYW5kIGB1aS5SYCBpcyBhdmFpbGFibGUgaW4gdGhlIGdpdGh1YiBbYHJlcG9zaXRvcnkgZm9yIHRoaXMgdHV0b3JpYWxgXShodHRwczovL2dpdGh1Yi5jb20vYW1jcmlzYW4vRXBpRGVzaWduUGF0dGVybikuCgoKIyBQcmVwYXJpbmcgdGhlIGRhdGEgZm9yIGFuYWx5c2lzCgpJbiB0aGlzIGxhc3Qgc2VjdGlvbiwgSSd2ZSBwcm92aWRlZCB0aGUgbml0eSBncml0dHkgZGV0YWlscyBvZiB3aGF0IGl0IHRvb2sgdG8gZXh0cmFjdCB0aGUgbWV0YWRhdGEgZnJvbSB0aGUgcGh5bG9nZW5ldGljIHRyZWUgdG8gbWFrZSBpdCB1c2FibGUgaW4gUi4KCmBgYHtyIG1ldGFQcmVwLGV2YWw9RkFMU0Usd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbXlUcmVlIDwtIHJlYWQubmV4dXMoIi4vYW5hbHlzaXMvTWFrb25hLnRyZWUiKSAKCnBUcmVlPC1nZ3RyZWUobXlUcmVlKQoKIyBJIGFsc28gd2FudCB0byBnZXQgb3V0IHRoZSBtZXRhZGF0YSAKCnJhd01ldGE8LWdldC50cmVlKG15VHJlZSkkdGlwLmxhYmVsICMgZm9yIHRoZXNlIGZpbGVzIGFsbCB0aGUgSU5GTyBpcyB0aGUgdHJlZSBub2RlIGxhYmVsCgpyYXdNZXRhPC1zYXBwbHkocmF3TWV0YSxmdW5jdGlvbih4KXtzdHJzcGxpdCh4LCJcXHwiKX0gJT4lIHVubGlzdCgpKSAlPiUgdCgpCnJhd01ldGE8LWNiaW5kKHJvd25hbWVzKHJhd01ldGEpLHJhd01ldGEpCgpjb2xuYW1lcyhyYXdNZXRhKTwtYygiSUQiLCJPUkciLCJMQUIiLCJMQUIyIiwiTEFCMyIsIkNvdW50cnkiLCJSZWdpb24iLCJSZWdpb24yIiwiUHJvdG9jb2wiLCJEYXRlIikgI25vdyBpdHMgYSBkYXRhIGZyYW1lCgptZXRhZGF0YTwtZGF0YS5mcmFtZShyYXdNZXRhKQoKI2Zvcm1hdHRpbmcgdGhlIGRhdGUgYSBsaXR0bGUgYml0Cm1ldGFkYXRhJERhdGU8LXltZChhcy5jaGFyYWN0ZXIobWV0YWRhdGEkRGF0ZSkpCm1ldGFkYXRhJFllYXJNb250aDwtZm9ybWF0KG1ldGFkYXRhJERhdGUsIiVZLSVtIikgI2ZvciBxdWljayBhY2Nlc3MKCiNub3cgbGV0cyBkcmF3IHNvbWUgdHJlZXMhCiN2ZXJ5IG5pY2UsIHRoZSB0cmVlIGlzIGNvbG91cmVkIGFjY29yZGluZyB0byB0aGUgY291bnRyeQpwVHJlZSAlPCslIG1ldGFkYXRhICsgZ2VvbV90aXBwb2ludChhZXMoY29sb3I9WWVhck1vbnRoKSxzaXplPTUsIGFscGhhPTAuMzUpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpCgojd2hhdCBhYm91dCBhIHJhZGlhbCB0cmVlPyAteWVzLCBpdCB3b3JrcywgYXdlc29tZSwgZ2d0cmVlIGlzIGEgZ3JlYXQgcGFja2FnZQpwVHJlZTwtZ2d0cmVlKG15VHJlZSxsYXlvdXQ9ImNpcmN1bGFyIikKcFRyZWUgJTwrJSBtZXRhZGF0YSsgZ2VvbV90aXBwb2ludChhZXMoY29sb3I9Q291bnRyeSksc2l6ZT01LCBhbHBoYT0wLjM1KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKQoKCiNzbyB0byBhZGQgZ2VvZ3JhcGhpYyBkYXRhLCBJIG5lZWQgdG8gZG8gYSBiaXQgbW9yZSBwcm9jZXNzaW5nIGhlcmUgdG8gc3RvcmUgY28tb3JkaW5hdGVzCiNJIGtub3cgdGhlIGNvdW50cmllcyBhbmQgR3VpbmVhLCBTaWVycmEgTGVvbmUsIGFuZCBMaWJlcmlhLCBzbyBJJ2xsIGxvb2sgdGhvc2UgdXAKZGZDb3VudHJ5PC1kYXRhLmZyYW1lKENvdW50cnk9YygiR0lOIiwiU0xFIiwiTEJSIiksCiAgICAgICAgICAgICAgICAgICAgICBsb25nTmFtZT1jKCJHdWluZWEiLCJTaWVycmEgTGVvbmUiLCJMaWJlcmlhIiksCiAgICAgICAgICAgICAgICAgICAgICBnZW9jb2RlKGMoIkd1aW5lYSIsIlNpZXJyYSBMZW9uZSIsIkxpYmVyaWEiKSxvdXRwdXQ9ImxhdGxvbiIpKQpjb2xuYW1lcyhkZkNvdW50cnkpPC0gYygiQ291bnRyeSIsImNvdW50cnlMb25nTmFtZSIsImNvdW50cnlfbG9uIiwiY291bnRyeV9sYXQiKQoKCiNsZXQncyBqb2ludCB0aGlzIGRhdGEgd2l0aCB0aGUgcmVzdCBvZiB0aGUgbWV0YWRhdGEsIEknbSBnb2luZyB0byBuZWVkIHRoZSBsb25nIG5hbWVzIHRvIGdldCB0aGUgYWNjdXJhdGUgcmVnaW9uCiN0ZXN0MjwtYmFzZTo6bWVyZ2UoeCA9IG1ldGFkYXRhLCB5ID0gZGZDb3VudHJ5LCBieSA9ICJDb3VudHJ5IixhbGwueD1UKSwgcHJvYmxlbSBpcyBpdCBlZmZlY3RzIGdndHJlZQoKI2ZvciByZXNvbnMgdW5rbm93biB0byBtZSwgbWVyZ2UgaXMgZG9pbmcgc29tZXRoaW5nIGJhZCB0byB0aGUgZGF0YSBmcmFtZSwgc28gSSd2ZSBoYXZlIHRvIGltcGxlbWVudCB0aGlzIGhhY2sKbWVyZ2VkQ291bnRyeTwtc2FwcGx5KG1ldGFkYXRhJENvdW50cnksZnVuY3Rpb24oeCl7CiAgdGVzdDwtZGZDb3VudHJ5ICU+JSBmaWx0ZXIoQ291bnRyeT09YXMuY2hhcmFjdGVyKHgpKSAlPiUgc2VsZWN0KGNvdW50cnlMb25nTmFtZSxjb3VudHJ5X2xvbixjb3VudHJ5X2xhdCkKICBjKGFzLmNoYXJhY3Rlcih0ZXN0WzEsMV0pLHRlc3RbMSwyXSx0ZXN0WzEsM10pCn0pICU+JSB0KCkgJT4lIGRhdGEuZnJhbWUKCmNvbG5hbWVzKG1lcmdlZENvdW50cnkpPC1jKCJjb3VudHJ5TG9uZ05hbWUiLCJjb3VudHJ5X2xvbiIsImNvdW50cnlfbGF0IikKCgptZXRhZGF0YTwtY2JpbmQobWV0YWRhdGEsbWVyZ2VkQ291bnRyeSkKCnJlZ2lvblN0cmluZzwtbWV0YWRhdGEgJT4lIAogIGZpbHRlcihSZWdpb24gIT0nPycpICU+JSAKICBtdXRhdGUoc2VhcmNoU3RyaW5nID0gcGFzdGUoUmVnaW9uLGNvdW50cnlMb25nTmFtZSxzZXA9IiwgIikpICU+JSAKICBzZWxlY3QoUmVnaW9uLHNlYXJjaFN0cmluZykgJT4lCiAgdW5pcXVlKCkKCmRmUmVnaW9uPC1kYXRhLmZyYW1lKFJlZ2lvbiA9IHJlZ2lvblN0cmluZyRSZWdpb24sCiAgICAgICAgICAgICAgICAgICAgIGdlb2NvZGUodW5pcXVlKHJlZ2lvblN0cmluZyRzZWFyY2hTdHJpbmcpKSkKY29sbmFtZXMoZGZSZWdpb24pPC1jKCJSZWdpb24iLCJyZWdpb25fbG9uIiwicmVnaW9uX2xhdCIpCgojZm9yIHJlc29ucyB1bmtub3duIHRvIG1lLCBtZXJnZSBpcyBkb2luZyBzb21ldGhpbmcgYmFkIHRvIHRoZSBkYXRhIGZyYW1lLCBzbyBJJ3ZlIGhhdmUgdG8gaW1wbGVtZW50IHRoaXMgaGFjawptZXJnZWRSZWdpb248LXNhcHBseShtZXRhZGF0YSRSZWdpb24sZnVuY3Rpb24oeCl7CiAgdGVzdDwtZGZSZWdpb24gJT4lIGZpbHRlcihSZWdpb249PWFzLmNoYXJhY3Rlcih4KSkgJT4lIHNlbGVjdChyZWdpb25fbG9uLHJlZ2lvbl9sYXQpCiAgYyhhcy5jaGFyYWN0ZXIodGVzdFsxLDFdKSx0ZXN0WzEsMl0pCn0pICU+JSB0KCkgJT4lIGRhdGEuZnJhbWUoKQpjb2xuYW1lcyhtZXJnZWRSZWdpb24pPC1jKCJyZWdpb25fbG9uIiwicmVnaW9uX2xhdCIpCgptZXRhZGF0YTwtY2JpbmQobWV0YWRhdGEsbWVyZ2VkUmVnaW9uKQoKI25vdyBJIGhhdmUgdG8gZml4IGFsbCBvZiB0aGUgbnVtYmVyIGlzc3VlcwptZXRhZGF0YSRjb3VudHJ5X2xvbjwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobWV0YWRhdGEkY291bnRyeV9sb24pKQptZXRhZGF0YSRjb3VudHJ5X2xhdDwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobWV0YWRhdGEkY291bnRyeV9sYXQpKQoKbWV0YWRhdGEkcmVnaW9uX2xvbjwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobWV0YWRhdGEkcmVnaW9uX2xvbikpCm1ldGFkYXRhJHJlZ2lvbl9sYXQ8LWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG1ldGFkYXRhJHJlZ2lvbl9sYXQpKQoKc2F2ZVJEUyhmaWxlPSJkYXRhL2Vib2xhX21ldGFkYXRhLlJEUyIsbWV0YWRhdGEpICNpdCBhbGwgd29ya3Mgbm93LCB0aGF0IHdhcyBhbm5veWluZwoKYGBgCgoK