<<BACK

(Remi/David) Now, if you remember what we did yesterday, we loaded and cleaned my larval abundance data, but I did not follow the principles of ‘tidy’ data when I uploaded my data.

Again, this data comes from one of Remi’s PhD thesis chapters1 that was part of the first CHONe. We cleaned in the Data cleaning and raw data management and is also archived on Dryad so we would have cleaning to do!

Reminder: The ‘tidy’ concept is something we will revisit in the R section, but the rules of tidy data are2:

  1. Each variable you measure should be in one column
  2. Each different observation of that variable should be in a different row
  3. There should be one table for each “kind” of variable
  4. If you have multiple tables, they should include a column in the table that allows them to be linked

While it was certainly easier to enter the abundance data in a spreadsheet by giving each species a column, this does not abide by the ‘tidy’ rules and we will have a much easier time analysing and plotting our data if we first ‘tidy’ the data.

# let's load all packages and the data before we begin
library(sf)
library(raster)
library(marmap)
library(robis)
library(gridExtra)
library(tidyverse)
larvalAbundance <- read.csv("data/larvalAbundanceClean.csv", stringsAsFactors = FALSE)
head(larvalAbundance)

The tidyverse ‘meta’ package

The tidyverse is a package that is made up of other packages commonly used together for data analysis and vizualization. You can find much more detail in the book R for Data Science by Garrett Grolemund and Hadley Wickham. Below we will learn about 3 of them:

  • tidyr for tidying data
  • dplyr for manipulating data
  • ggplot2 for plotting data

The tidyr package

This aptly named package will allow us to tidy our data. Our data is currently in the wide format since we have multiple observations of species abundances in seperate columns. We want our data to be in the long format in order to facilitate analysis and plotting (and to abide by the ‘tidy’ rules).

We currently have a few ID type variable (i.e. they are not observations; year,month,day,time,depth,site,long,lat) and an observational variable for each species.

names(larvalAbundance)
 [1] "year"                     "month"                    "day"                      "time"                    
 [5] "depth"                    "locationID"               "decimalLatitude"          "decimalLongitude"        
 [9] "Astyris.lunata"           "Bittiolum.alternatum"     "Margarites.spp."          "Arrhoges.occidentalis"   
[13] "Diaphana.minuta"          "Crepidula.spp."           "Other.Gastropods"         "Mytilus.spp."            
[17] "Modiolus.modiolus"        "Anomia.simplex"           "Other.Bivalve"            "Electra.pilosa"          
[21] "Membranipora.membranacea" "Carcinus.maenas"          "Cancer.irroratus"         "Neopanopeus.sayi"        
[25] "Crangon.septemspinosa"   
larvalAbundanceLong <- gather(larvalAbundance,                                  # the old data frame
                              key = "scientificName",                           # what the column of old column names will be called
                              value = "larvalAbundanceIndPerm3",                # what the gathered numbers will be called
                              Astyris.lunata:Crangon.septemspinosa)             # which columns  to gather
# alternatively, I could of specified which columns not to include instead
larvalAbundanceLong <- gather(larvalAbundance,                                  # the old data frame
                              key = "scientificName",                           # what the column of old column names will be called
                              value = "larvalAbundanceIndPerm3",                # what the gathered numbers will be called
                              -year,-month,-day,-time,-depth,-locationID,-decimalLongitude,-decimalLatitude)  # which columns not to gather
head(larvalAbundanceLong)                           

We can also clean up and/or separate the scientificName column

# first let's swap multiple species 
larvalAbundanceLong <- separate(larvalAbundanceLong,           #old data frame
                                col = scientificName,          # column to be seperated
                                into = c("genus","species"),   # to be seperated into
                                extra = "merge",               # if there are too many ".", what to do with the extra column
                                remove = FALSE,                # whether to delete the seperated column
                                sep = "\\.")                   # regex for ".", but need escape character "\\" since . is a wildcard
# Let's check out our work
head(larvalAbundanceLong)
# genus and species don't look terrible, but scientificName still has that pesky period
# we could use gsub again, but the regex is tricky since there are spp. for species
# pasting the genus and species together will be much easier
larvalAbundanceLong$scientificName <- paste(larvalAbundanceLong$genus,larvalAbundanceLong$species,sep = " ")
# also, spp. is not a species designation, let's get rid of them
larvalAbundanceLong$species[larvalAbundanceLong$species=="spp."] <- NA
# Let's check out our work again
head(larvalAbundanceLong)

The dplyr package

Manipulation of dataframes means many things to many researchers, we often select certain observations (rows) or variables (columns), we often group the data by a certain variable(s), or we even calculate summary statistics. We can do these operations using the normal base R operations:

mean(larvalAbundanceLong[larvalAbundanceLong$scientificName == "Astyris lunata", "larvalAbundanceIndPerm3"])
[1] 13.85279
mean(larvalAbundanceLong[larvalAbundanceLong$scientificName == "Bittiolum alternatum", "larvalAbundanceIndPerm3"])
[1] 1.713291
mean(larvalAbundanceLong[larvalAbundanceLong$scientificName == "Arrhoges occidentalis", "larvalAbundanceIndPerm3"])
[1] 0.7773257

But this isn’t very nice because there is a fair bit of repetition. Repeating yourself will cost you time, both now and later, and potentially introduce some nasty bugs.

Luckily, the dplyr package provides a number of very useful functions for manipulating dataframes in a way that will reduce the above repetition, reduce the probability of making errors, and probably even save you some typing. As an added bonus, you might even find the dplyr grammar easier to read.

Here we’re going to cover 5 of the most commonly used functions as well as using pipes (%>%) to combine them.

  1. select()
  2. filter()
  3. group_by()
  4. summarize()
  5. mutate()

Using select()

If, for example, we wanted to move forward with only a few of the variables in our dataframe we could use the select() function. This will keep only the variables you select.

yearAndSpecies <- select(larvalAbundanceLong,year,species,genus,larvalAbundanceIndPerm3)

If we open up yearAndSpecies we’ll see that it only contains the year, and the larval abundance for each species.

Above we used ‘normal’ grammar, but the strengths of dplyr lie in combining several functions using pipes (%>%). Since the pipes grammar is unlike anything we’ve seen in R before, let’s repeat what we’ve done above using pipes.

yearTaxonAbun <- larvalAbundanceLong %>% select(year,species,genus,larvalAbundanceIndPerm3)

To help you understand why we wrote that in that way, let’s walk through it step by step. In both cases, the left side of <- is yearTaxonAbun. First we summon the larvalAbundanceLong dataframe and pass it on, using the pipe symbol %>%, to the next step, which is the select() function. In this case we don’t specify which .data argument we use in the select() function since in gets that from the previous pipe. Essentially, the output of what is written before the pipe becomes the first argument, in this case .data, of the function after the pipe. Fun Fact: There is a chance you have encountered pipes before in the shell. In R, a pipe symbol is %>% while in the shell it is | but the concept is the same!

Pro-tip

Did you get an error while running the select() function? Something like: Error in (function (classes, fdef, mtable) : unable to find an inherited method for function ‘select’ for signature ‘"data.frame"’ That’s because many packages have a select() function; In this case, it’s the raster package causing problems. It is possible to get around that problem the ‘hacky’ way by loading tidyverse after all the other packages so that the dplyr (which is in the tidyverse) version of select sort of overwrites the one loaded from raster.

Alternatively, you can designate which package using the :: (e.g. package::function()) when it’s not clear. yearTaxonAbun <- larvalAbundanceLong %>% dplyr::select(year,species,genus,larvalAbundanceIndPerm3)

Using filter()

If we now wanted to move forward with the above, but only with species for which we have a valid genus and species, we can combine select and filter

yearTaxonAbunValid <- larvalAbundanceLong %>%
    select(year,species,genus,larvalAbundanceIndPerm3) %>% 
    filter(genus!="Other") %>% 
    filter(!is.na(species))
# we can also string the two filter operations into one using the and symbol '&'
yearTaxonAbunValid <- larvalAbundanceLong %>%
    select(year,species,genus,larvalAbundanceIndPerm3) %>% 
    filter(genus!="Other" & !is.na(species))

** Challenge 1 **

Write a single command (which can span multiple lines and includes pipes) that will produce a dataframe that has the values for larvalAbundanceIndPerm3 for all species at locationID 5, but not for other sites. I also want locationID, and lat/long removed from the output. How many rows does your dataframe have and why? Solution

Using group_by()

Now, we were supposed to be reducing the error prone repetitiveness of what can be done with base R, but up to now we haven’t done that since we would have to repeat the above for each species. Instead of filter(), which will only pass observations that meet your criteria (in the above: locationID==5), we can use group_by(), which will essentially use every unique criteria that you could have used in filter.

str(larvalAbundanceLong)
'data.frame':   1343 obs. of  12 variables:
 $ year                   : int  2008 2008 2008 2008 2008 2008 2008 2008 2008 2008 ...
 $ month                  : int  8 8 8 8 8 8 8 8 8 8 ...
 $ day                    : int  7 8 7 7 7 7 7 8 8 8 ...
 $ time                   : chr  "10:40:00" "9:05:00" "12:14:00" "13:47:00" ...
 $ depth                  : int  3 12 12 12 12 12 12 12 12 12 ...
 $ locationID             : int  5 5 6 7 8 9 10 11 12 13 ...
 $ decimalLatitude        : num  45.8 45.8 45.8 45.8 45.8 ...
 $ decimalLongitude       : num  -61.9 -61.9 -61.8 -61.7 -61.6 ...
 $ scientificName         : chr  "Astyris lunata" "Astyris lunata" "Astyris lunata" "Astyris lunata" ...
 $ genus                  : chr  "Astyris" "Astyris" "Astyris" "Astyris" ...
 $ species                : chr  "lunata" "lunata" "lunata" "lunata" ...
 $ larvalAbundanceIndPerm3: num  4.478 12.548 31.834 21.162 0.324 ...
str(larvalAbundanceLong %>% group_by(locationID))
Classes ‘grouped_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 1343 obs. of  12 variables:
 $ year                   : int  2008 2008 2008 2008 2008 2008 2008 2008 2008 2008 ...
 $ month                  : int  8 8 8 8 8 8 8 8 8 8 ...
 $ day                    : int  7 8 7 7 7 7 7 8 8 8 ...
 $ time                   : chr  "10:40:00" "9:05:00" "12:14:00" "13:47:00" ...
 $ depth                  : int  3 12 12 12 12 12 12 12 12 12 ...
 $ locationID             : int  5 5 6 7 8 9 10 11 12 13 ...
 $ decimalLatitude        : num  45.8 45.8 45.8 45.8 45.8 ...
 $ decimalLongitude       : num  -61.9 -61.9 -61.8 -61.7 -61.6 ...
 $ scientificName         : chr  "Astyris lunata" "Astyris lunata" "Astyris lunata" "Astyris lunata" ...
 $ genus                  : chr  "Astyris" "Astyris" "Astyris" "Astyris" ...
 $ species                : chr  "lunata" "lunata" "lunata" "lunata" ...
 $ larvalAbundanceIndPerm3: num  4.478 12.548 31.834 21.162 0.324 ...
 - attr(*, "vars")=List of 1
  ..$ : symbol locationID
 - attr(*, "drop")= logi TRUE
 - attr(*, "indices")=List of 16
  ..$ : int  0 1 12 23 34 45 46 62 63 79 ...
  ..$ : int  2 13 24 35 47 64 81 92 103 114 ...
  ..$ : int  3 14 25 36 48 65 82 93 104 115 ...
  ..$ : int  4 15 26 37 49 66 83 94 105 116 ...
  ..$ : int  5 16 27 38 50 67 84 95 106 117 ...
  ..$ : int  6 17 28 39 51 68 85 96 107 118 ...
  ..$ : int  7 18 29 40 52 69 86 97 108 119 ...
  ..$ : int  8 19 30 41 53 70 87 98 109 120 ...
  ..$ : int  9 20 31 42 54 71 88 99 110 121 ...
  ..$ : int  10 21 32 43 55 72 89 100 111 122 ...
  ..$ : int  11 22 33 44 56 73 90 101 112 123 ...
  ..$ : int  57 74 136 153 215 232 294 311 373 390 ...
  ..$ : int  58 75 137 154 216 233 295 312 374 391 ...
  ..$ : int  59 76 138 155 217 234 296 313 375 392 ...
  ..$ : int  60 77 139 156 218 235 297 314 376 393 ...
  ..$ : int  61 78 140 157 219 236 298 315 377 394 ...
 - attr(*, "group_sizes")= int  153 102 102 102 102 102 102 102 102 102 ...
 - attr(*, "biggest_group_size")= int 153
 - attr(*, "labels")='data.frame':  16 obs. of  1 variable:
  ..$ locationID: int  5 6 7 8 9 10 11 12 13 14 ...
  ..- attr(*, "vars")=List of 1
  .. ..$ : symbol locationID
  ..- attr(*, "drop")= logi TRUE

You will notice that the structure of the dataframe where we used group_by() (grouped_df) is not the same as the original larvalAbundanceLong (data.frame). A grouped_df can be thought of as a list where each item in the listis a data.frame which contains only the rows that correspond to the a particular value locationID (at least in the example above).

Using summarize()

The above was a bit on the uneventful side because group_by() much more exciting in conjunction with summarize(). This will allow use to create new variable(s) by using functions that repeat for each of the locationID-specific data frames. That is to say, using the group_by() function, we split our original dataframe into multiple pieces, then we can run functions (e.g. mean() or sd()) within summarize().

IndPerm3_byLocationID <- larvalAbundanceLong %>%
    group_by(locationID) %>%
    summarize(mean_larvalAbundanceIndPerm3 = mean(larvalAbundanceIndPerm3))

That allowed us to calculate the mean larval abundance for each location, but it gets so much better!

** Challenge 2 **

Calculate the mean larval abundance per locationID. Which has the highest mean larval abundance and which has the lowest mean larval abundance? Solution

The function group_by() allows us to group by multiple variables. Let’s group by locationID and scientificName.

IndPerm3_byLocationID_bySpp <- larvalAbundanceLong %>%
    group_by(locationID,scientificName ) %>%
    summarize(mean_larvalAbundanceIndPerm3 = mean(larvalAbundanceIndPerm3))

That is already quite powerful, but it gets even better! You’re not limited to defining 1 new variable in summarize().

IndPerm3_byLocationID_bySpp <- larvalAbundanceLong %>%
    group_by(locationID,scientificName ) %>%
    summarize(mean_larvalAbundanceIndPerm3 = mean(larvalAbundanceIndPerm3),
              sd_larvalAbundanceIndPerm3 = sd(larvalAbundanceIndPerm3),
              mean_depth = mean(depth),
              sd_depth= sd(depth))

Using mutate()

We can also create new variables prior to (or even after) summarizing information using mutate().

IndPerm3_byLocationID_bySpp <- larvalAbundanceLong %>%
    mutate(larvalAbundanceIndPercm3=larvalAbundanceIndPerm3*1000000) %>% 
    group_by(locationID,scientificName ) %>%
    summarize(mean_larvalAbundanceIndPercm3 = mean(larvalAbundanceIndPercm3),
              sd_larvalAbundanceIndPercm3 = sd(larvalAbundanceIndPercm3),
              mean_depth = mean(depth),
              sd_depth= sd(depth))

Plotting

R comes with basic plotting function, usually referred to as ‘base plot’, e.g:

plot(larvalAbundance$Margarites.spp.,larvalAbundance$Diaphana.minuta)

However, general concensus seems to be that ggplot2 (part of the tidyverse) is the way to go for plotting. I (Remi) have mixed feelings about this package (my age is showing again). It does make making quick plots easier and is compatible with the rest of the tidyverse, but until recently, ggplot2 really struggled with any spatial plotting (see ‘The classic and the cutting edge: sp vs sf’) so I had to know the base plot anyway. I still think base plot is more customizeable, but it may also be that I have not sufficiently mastered the ggplot world. We’re mostly going to cover ggplot2, but be aware that there are other plotting paradigms in R (e.g. base, lattice, etc)

The ggplot2 package

A system for ‘declaratively’ creating graphics, based on “The Grammar of Graphics”. You provide the data, tell ‘ggplot2’ how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details

So for the same plot as above, let me walk you through each line below step by step. The first line creates a new blank ggplot plot. Some people set aes() and data in ggplot(), but since different layers can take different aes() and data, I like to set those in the geom functions.

Unlike other tidyverse functions, you can tie together ggplot2 functions with + instead of %>%, but otherwise it works the same. So to our blank plot, we’re adding geom_point() which is the function for a scatterplot. We’re telling geom_point() that all the data it needs should be in larvalAbundance and what aesthetic mapping we want (aes()) which sets the variables used in the plot and takes arguments like x, y, colour, size.

Finally, I set the theme as theme_classic because the default plots are ugly! (try it, I dare you) #

ggplot() +
    geom_point(data = larvalAbundance, aes(x=Margarites.spp.,y=Diaphana.minuta)) +
    theme_classic()

Yeah, that was way more code than base plot just to get a simple plot! But now that we have the basic ggplot skeleton, our investment is going to pay off because it’s now super easy to modify. Let’s assign different colours for different depths.

ggplot() +
    geom_point(data = larvalAbundance, aes(x=Margarites.spp.,y=Diaphana.minuta,colour=as.character(depth))) +
    theme_classic()

I had to convert year and depth to characters since ggplot assumes that numeric variables are continous. Let’s combine what we learned above about the tidyverse.

# prepare data for plotting
larvalAbundancePlot <- larvalAbundance %>% 
    mutate(Year = as.character(year),
           Depth = as.character(depth))
# plot new data
ggplot() +
    geom_point(data=larvalAbundancePlot,aes(x=Margarites.spp.,y=Diaphana.minuta,colour=Depth,shape=Year)) +
    theme_classic()

Let’s add a linear regression and make the axes log scale:

ggplot() +
    geom_point(data=larvalAbundancePlot,aes(x=Margarites.spp.,y=Diaphana.minuta,colour=Depth,shape=Year)) +
    geom_smooth(method="lm")+
    scale_x_log10()+
    scale_y_log10()+
    theme_classic()

Try commenting out (#) some of those lines to see what they do.

Now that you understand how that works, let’s try a few different plot types

# prepare long data for plotting
larvalAbundanceLongPlot <- larvalAbundanceLong %>% 
    mutate(Year = as.character(year),
           Depth = as.character(depth))
# plot new data
ggplot() +
    geom_boxplot(data=larvalAbundanceLongPlot,aes(x=Depth,y=larvalAbundanceIndPerm3,fill=Depth)) +
    scale_y_log10()+
    theme_classic()

Ready to get your mind blown? By adding 1 line to the above, we can have a plot for each species

ggplot() +
    geom_boxplot(data=larvalAbundanceLongPlot,aes(x=Depth,y=larvalAbundanceIndPerm3,fill=Depth)) +
    scale_y_log10()+
    facet_wrap(~scientificName)+
    theme_classic()

Or swapping out the geom gives you a completely different (better) plot.

ggplot() +
    geom_violin(data=larvalAbundanceLongPlot,aes(x=Depth,y=larvalAbundanceIndPerm3,fill=Depth)) +
    scale_y_log10()+
    facet_wrap(~scientificName)+
    theme_classic()

Or swapping out the geom again and we can map the abundance.

# prepare long data for plotting
larvalAbundanceLongPlot <- larvalAbundanceLong %>% 
    group_by(scientificName,decimalLongitude,decimalLatitude) %>% 
    summarize(mean_IndPerm3 = mean(larvalAbundanceIndPerm3))
# plot new mean data
ggplot() +
    geom_point(data=larvalAbundanceLongPlot,aes(x=decimalLongitude,y=decimalLatitude,size=log10(mean_IndPerm3))) +
    facet_wrap(~scientificName)+
    theme_classic()

Let’s add a bit more to these maps with some proper mapping tools. But before we move away from ggplot, bookmark this excellent “R Graph Catalog”. You can visually find a graph you want to make and get the code! Super useful!

Mapping

You can always plot any spatial data and use latitude and longitude as normal Cartesian coordinates as we did above, but if you want to do anything with projections, or other GIS type operations (buffer, intersection, overlaps, etc), you really need to dig a little deeper.

Projection and datum information in R in both sp and sf (as well as many other applications) are handled by the proj.4 standard. We won’t go in to deep into the ‘how to choose a projection’, but there is substantial information regarding the parameters on the proj.4 website and this Projection Wizard is super helpful for recommending projection (and producing formatted proj4 strings) based on the extent of your map and what you want to optimize for (i.e. Equal-area, Conformal, Equidistant, Compromise).

Be aware that in most cases R will throw an error if you are trying to use multiple spatial objects with different transformations, so remember to transform them to a common projection before trying to combine them. Also, remember that some projections have units of latitude and longitude and some have units of meters and they are of course incompatible.

The classic and the cutting edge: sp vs sf

Before November 2016, if you wanted to do any spatial tasks in R your choice was a simple one. You needed to use the sp package (along with rgeos, and rgdal). Now, the sf package is replacing that set of packages, and that’s a good thing because it allows for faster computation and it is compatible with the tidyverse. However, many of the helper packages that were built to interact with sp have not been upgraded to sf (yet).

The sp package

The bread and butter ‘Spatial’ object class from the sppackage are of the form SpatialBLANKDataFrame where blank can be Points, Lines, Polygons, etc. Let’s create a SpatialPointsDataFrame from our larvalAbundanceLong

# first make spatial points and set the projection (more on that below)
spoints <- SpatialPoints(coords = larvalAbundanceLong[,c("decimalLongitude","decimalLatitude")],
                         proj4string = CRS("+proj=longlat +datum=WGS84"))
# then make a SpatialPointsDataFrame by addint the data frame back in
spointsdf <- SpatialPointsDataFrame(spoints,larvalAbundanceLong)
# now for each point in spointsdf there is a row in the data frame which can be accessed by spointsdf@data, or if you want a particular variable spointsdf$year (or spointsdf@data$year), but the data and the geometries are a little disjoint.
# plot it
plot(spointsdf)

That’s not particularly useful without a basemap, so let’s use the raster package to get some high resolution province level boundaries data (not the only way to get coastlines, but one of my favorite).

Canada <- getData('GADM', country="CAN", level=1)
plot(Canada)
# this is a little excessive since we only need Nova Scotia
NS <- Canada[Canada$NAME_1=="Nova Scotia",]
plot(NS,add=T,col='blue')

Now let’s combine my data with the coastline

# see the bounding box of the larval data
bbox(spointsdf)
                       min       max
decimalLongitude -61.88518 -61.57103
decimalLatitude   45.71147  46.05218
# transform both Spatial objects
proj_aea <- "+proj=aea +lat_1=45.34229371956944 +lat_2=46.154426884765485 +lon_0=-61.70471191406251"
spointsdf_aea <- spTransform(spointsdf,proj_aea)
NS_aea <- spTransform(NS,proj_aea)
# see the bounding box of the larval data after projection
bbox(spointsdf_aea)
                        min        max
decimalLongitude  -13996.94   10365.71
decimalLatitude  4714435.00 4752308.03
plot(spointsdf_aea,pch=16, xlim = c(-15000,12000), ylim = c(4705000,4755000))
plot(NS_aea, col = 'grey',add=T)

You can find more info about the other Spatial classes in my GIS tutorial I wrote before sf was created.

The sf package

The sf package is an an R implementation of “Simple Features” (officially Simple Feature Access) which is both an Open Geospatial Consortium (OGC) and International Organization for Standardization (ISO) standard ISO 19125 that specifies a common storage and access model of mostly two-dimensional geometries (point, line, polygon, multi-point, multi-line, etc.) used by geographic information systems.

The bread and butter object classes in the sf package are sf (created by st_sf()) which are effectively a normal data frame with the addition of one (or more) simple features list column (sfc; created by st_sfc()). This sfc column, which is really just another column in your data frame, contains the geometric information that gets plotted on a map. This geometric information is stored as a simple feature which can take the form of polygon(s) (created by st_polygon() or st_multipolygon() ), line(s) (created by st_st_linestring() or st_multist_linestring() ), or point(s) (created by st_point() or st_multipoint() )

Let’s start by turning our larvalAbundanceLong into an sf object and transform it into our desired projection in 1 command (reminder: sf is compatible with the tidyverse):

larvalAbundanceLong_sf <- larvalAbundanceLong %>% 
    st_as_sf(coords = c("decimalLongitude", "decimalLatitude"),
             crs="+proj=longlat +datum=WGS84",
             remove=FALSE) %>% 
    st_transform(proj_aea)
ggplot()+
    geom_sf(data = larvalAbundanceLong_sf)

As you may have noticed, geom_sf does not need a defined aes() since is already knows that latitude is your y-axis and longitude is the x-axis. You can of course toggle group, colour, size, and fill in the aes based on other columns in your data frame.

Let’s plot is with a basemap! raster only provides the GADM coastline data in sp format, but we can convert with st_as_sf() and then filter() to get just the Maritime provinces with dplyr

MarProv_sf <- getData('GADM', country="CAN", level=1) %>% 
    st_as_sf() %>% 
    filter(NAME_1=="Nova Scotia"|
               NAME_1=="Prince Edward Island"|
               NAME_1=="New Brunswick")%>% 
    st_transform(proj_aea)
ggplot()+
    geom_sf(data = larvalAbundanceLong_sf)+
    geom_sf(data = MarProv_sf)

And to make things fun, let’s have one zoomed in plot with the dots, and a zoomed out plot with a ‘zoom box’

#define the corners (with a buffer)
larvalbbox <- st_buffer(larvalAbundanceLong_sf, dist = 10000) %>% 
    st_bbox()

#create a matrix that traces a line the whole way around your box
bbsfc <- cbind(
            c(larvalbbox[1],larvalbbox[3],larvalbbox[3],larvalbbox[1],larvalbbox[1]),
            c(larvalbbox[2],larvalbbox[2],larvalbbox[4],larvalbbox[4],larvalbbox[2])
        ) %>% 
    list() %>%                  # put that matrix in a list
    st_polygon() %>%            # put that list in a simple features polygon
    st_sfc(crs = proj_aea) %>%  # put that polygon into a simple features column and give projection info
    st_sf(name="Study Site", geometry=.)    # finally make it an sf object

# plot it!
p1 <- ggplot()+
    geom_sf(data = MarProv_sf)+
    geom_sf(data = bbsfc, size = 2, fill = "transparent")

p2 <- ggplot()+
    geom_sf(data = larvalAbundanceLong_sf)+
    geom_sf(data = MarProv_sf) +
    coord_sf(xlim = larvalbbox[c(1,3)],ylim = larvalbbox[c(2,4)])

grid.arrange(p1,p2)

Finally, let’s bring facets back!

#define the corners (with a buffer)
larvalbbox <- st_buffer(larvalAbundanceLong_sf, dist = 10000) %>% 
    st_bbox()
#create a matrix that traces a line the whole way around your box
bbsfc <- cbind(
            c(larvalbbox[1],larvalbbox[3],larvalbbox[3],larvalbbox[1],larvalbbox[1]),
            c(larvalbbox[2],larvalbbox[2],larvalbbox[4],larvalbbox[4],larvalbbox[2])
        ) %>% 
    list() %>%                  # put that matrix in a list
    st_polygon() %>%            # put that list in a simple features polygon
    st_sfc(crs = proj_aea) %>%  # put that polygon into a simple features column and give projection info
    st_sf(name="Study Site", geometry=.)    # finally make it an sf object
# plot it!
p1 <- ggplot()+
    geom_sf(data = MarProv_sf)+
    geom_sf(data = bbsfc, size = 2, fill = "transparent")
p2 <- ggplot()+
    geom_sf(data = larvalAbundanceLong_sf)+
    geom_sf(data = MarProv_sf) +
    coord_sf(xlim = larvalbbox[c(1,3)],ylim = larvalbbox[c(2,4)])
grid.arrange(p1,p2)

# oh, and if you want to save a plot, pick your format, size, and resolution
ggsave(filename="speciesabundance.tiff", plot = p3, width = 20, height = 16.5, unit = "cm", dpi = 300)
ggsave(filename="speciesabundance.pdf", plot = p3, width = 20, height = 16.5, unit = "cm", dpi = 300)
ggsave(filename="speciesabundance.png", plot = p3, width = 20, height = 16.5, unit = "cm", dpi = 300)

Challenge answers

Challenge 1 solution

site5 <- larvalAbundanceLong %>%
   filter(locationID==5) %>%
   select(-locationID,-decimalLatitude,-decimalLatitude)

Note: The order of operations is very important in this case. If we used ‘select’ first, filter would not be able to find the variable continent since we would have removed it in the previous step.

Challenge 2 solution

IndPerm3_byLocationID <- larvalAbundanceLong %>%
   group_by(locationID) %>%
   summarize(mean_IndPerm3 = mean(larvalAbundanceIndPerm3)) %>% 
   filter(mean_IndPerm3 == min(mean_IndPerm3) | mean_IndPerm3 == max(mean_IndPerm3))

<<BACK


  1. Daigle RM, Metaxas A, deYoung B (2014) Bay-scale patterns in the distribution, aggregation and spatial variability of larvae of benthic invertebrates. Marine Ecology Progress Series 503:139-156. http://dx.doi.org/10.3354/meps10734

  2. Jeff Leek, The Elements of Data Analytic Style, Leanpub, 2015-03-02

LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgYW5kIFZpc3VhbGl6YXRpb24gaW4gUiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQpbPDxCQUNLXShodHRwczovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby8yMDE3LUNIT05lLURhdGEvKQ0KDQooUmVtaS9EYXZpZCkNCk5vdywgaWYgeW91IHJlbWVtYmVyIHdoYXQgd2UgZGlkIHllc3RlcmRheSwgd2UgbG9hZGVkIGFuZCBjbGVhbmVkIG15IGxhcnZhbCBhYnVuZGFuY2UgZGF0YSwgYnV0IEkgZGlkIG5vdCBmb2xsb3cgdGhlIHByaW5jaXBsZXMgb2YgJ3RpZHknIGRhdGEgd2hlbiBJIHVwbG9hZGVkIG15IGRhdGEuDQoNCkFnYWluLCB0aGlzIGRhdGEgY29tZXMgZnJvbSBvbmUgb2YgUmVtaSdzIFBoRCB0aGVzaXMgY2hhcHRlcnNbXjFdIHRoYXQgd2FzIHBhcnQgb2YgdGhlIGZpcnN0IENIT05lLiBXZSBjbGVhbmVkIGluIHRoZSBbRGF0YSBjbGVhbmluZyBhbmQgcmF3IGRhdGEgbWFuYWdlbWVudF0oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhL2NsZWFuaW5nLm5iLmh0bWwpIGFuZCBpcyBhbHNvIGFyY2hpdmVkIG9uIFtEcnlhZF0oaHR0cDovL2RhdGFkcnlhZC5vcmcvaGFuZGxlLzEwMjU1L2RyeWFkLjU5NDgyKSBzbyB3ZSB3b3VsZCBoYXZlIGNsZWFuaW5nIHRvIGRvIQ0KDQpbXjFdOiBEYWlnbGUgUk0sIE1ldGF4YXMgQSwgZGVZb3VuZyBCICgyMDE0KSBCYXktc2NhbGUgcGF0dGVybnMgaW4gdGhlIGRpc3RyaWJ1dGlvbiwgYWdncmVnYXRpb24gYW5kIHNwYXRpYWwgdmFyaWFiaWxpdHkgb2YgbGFydmFlIG9mIGJlbnRoaWMgaW52ZXJ0ZWJyYXRlcy4gTWFyaW5lIEVjb2xvZ3kgUHJvZ3Jlc3MgU2VyaWVzIDUwMzoxMzktMTU2LiBodHRwOi8vZHguZG9pLm9yZy8xMC4zMzU0L21lcHMxMDczNA0KDQoqKlJlbWluZGVyKio6DQpUaGUgJ3RpZHknIGNvbmNlcHQgaXMgc29tZXRoaW5nIHdlIHdpbGwgcmV2aXNpdCBpbiB0aGUgUiBzZWN0aW9uLCBidXQgdGhlIHJ1bGVzIG9mIHRpZHkgZGF0YSBhcmVbXjJdOg0KDQoxLiBFYWNoIHZhcmlhYmxlIHlvdSBtZWFzdXJlIHNob3VsZCBiZSBpbiBvbmUgY29sdW1uDQoyLiBFYWNoIGRpZmZlcmVudCBvYnNlcnZhdGlvbiBvZiB0aGF0IHZhcmlhYmxlIHNob3VsZCBiZSBpbiBhIGRpZmZlcmVudCByb3cNCjMuIFRoZXJlIHNob3VsZCBiZSBvbmUgdGFibGUgZm9yIGVhY2gg4oCca2luZOKAnSBvZiB2YXJpYWJsZQ0KNC4gSWYgeW91IGhhdmUgbXVsdGlwbGUgdGFibGVzLCB0aGV5IHNob3VsZCBpbmNsdWRlIGEgY29sdW1uIGluIHRoZSB0YWJsZSB0aGF0IGFsbG93cyB0aGVtIHRvIGJlIGxpbmtlZA0KDQpXaGlsZSBpdCB3YXMgY2VydGFpbmx5IGVhc2llciB0byBlbnRlciB0aGUgYWJ1bmRhbmNlIGRhdGEgaW4gYSBzcHJlYWRzaGVldCBieSBnaXZpbmcgZWFjaCBzcGVjaWVzIGEgY29sdW1uLCB0aGlzIGRvZXMgbm90IGFiaWRlIGJ5IHRoZSAndGlkeScgcnVsZXMgYW5kIHdlIHdpbGwgaGF2ZSBhIG11Y2ggZWFzaWVyIHRpbWUgYW5hbHlzaW5nIGFuZCBwbG90dGluZyBvdXIgZGF0YSBpZiB3ZSBmaXJzdCAndGlkeScgdGhlIGRhdGEuDQoNClteMl06IFtKZWZmIExlZWssIFRoZSBFbGVtZW50cyBvZiBEYXRhIEFuYWx5dGljIFN0eWxlLCBMZWFucHViLCAyMDE1LTAzLTAyXShodHRwczovL2xlYW5wdWIuY29tL2RhdGFzdHlsZSkNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCiMgbGV0J3MgbG9hZCBhbGwgcGFja2FnZXMgYW5kIHRoZSBkYXRhIGJlZm9yZSB3ZSBiZWdpbg0KbGlicmFyeShzZikNCmxpYnJhcnkocmFzdGVyKQ0KbGlicmFyeShtYXJtYXApDQpsaWJyYXJ5KHJvYmlzKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KbGFydmFsQWJ1bmRhbmNlIDwtIHJlYWQuY3N2KCJkYXRhL2xhcnZhbEFidW5kYW5jZUNsZWFuLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KaGVhZChsYXJ2YWxBYnVuZGFuY2UpDQpgYGANCg0KDQojIFRoZSBgdGlkeXZlcnNlYCAnbWV0YScgcGFja2FnZQ0KDQpUaGUgYHRpZHl2ZXJzZWAgaXMgYSBwYWNrYWdlIHRoYXQgaXMgbWFkZSB1cCBvZiBvdGhlciBwYWNrYWdlcyBjb21tb25seSB1c2VkIHRvZ2V0aGVyIGZvciBkYXRhIGFuYWx5c2lzIGFuZCB2aXp1YWxpemF0aW9uLiBZb3UgY2FuIGZpbmQgbXVjaCBtb3JlIGRldGFpbCBpbiB0aGUgYm9vayBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwOi8vcjRkcy5oYWQuY28ubnovKSBieSBHYXJyZXR0IEdyb2xlbXVuZCBhbmQgSGFkbGV5IFdpY2toYW0uIEJlbG93IHdlIHdpbGwgbGVhcm4gYWJvdXQgMyBvZiB0aGVtOg0KDQotIGB0aWR5cmAgZm9yIHRpZHlpbmcgZGF0YQ0KLSBgZHBseXJgIGZvciBtYW5pcHVsYXRpbmcgZGF0YQ0KLSBgZ2dwbG90MmAgZm9yIHBsb3R0aW5nIGRhdGENCg0KIyMgVGhlIGB0aWR5cmAgcGFja2FnZQ0KDQpUaGlzIGFwdGx5IG5hbWVkIHBhY2thZ2Ugd2lsbCBhbGxvdyB1cyB0byB0aWR5IG91ciBkYXRhLiBPdXIgZGF0YSBpcyBjdXJyZW50bHkgaW4gdGhlIHdpZGUgZm9ybWF0IHNpbmNlIHdlIGhhdmUgbXVsdGlwbGUgb2JzZXJ2YXRpb25zIG9mIHNwZWNpZXMgYWJ1bmRhbmNlcyBpbiBzZXBlcmF0ZSBjb2x1bW5zLiBXZSB3YW50IG91ciBkYXRhIHRvIGJlIGluIHRoZSBsb25nIGZvcm1hdCBpbiBvcmRlciB0byBmYWNpbGl0YXRlIGFuYWx5c2lzIGFuZCBwbG90dGluZyAoYW5kIHRvIGFiaWRlIGJ5IHRoZSAndGlkeScgcnVsZXMpLg0KDQoNCmBgYHtyIHdpZGV2c2xvbmcscmVzdWx0cz0nYXNpcycsZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnJlcXVpcmUoRGlhZ3JhbW1lUikNCmdyVml6KCdkaWdyYXBoIGh0bWwgew0KICAgICAgdGFibGUxIFtzaGFwZT1ub25lLCBtYXJnaW49MCwgbGFiZWw9PA0KICAgICAgPFRBQkxFIEJPUkRFUj0iMCIgQ0VMTEJPUkRFUj0iMCIgQ0VMTFNQQUNJTkc9IjEiIENFTExQQURESU5HPSI2Ij4NCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+SUQ8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiIENFTExQQURESU5HPSIwIj5hMTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmEyPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIiBDRUxMUEFERElORz0iMCI+YTM8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiPjE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiPjI8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiPjM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlMiBbc2hhcGU9bm9uZSwgbWFyZ2luPTAsbGFiZWw9PA0KICAgICAgPFRBQkxFIEJPUkRFUj0iMCIgQ0VMTEJPUkRFUj0iMCIgQ0VMTFNQQUNJTkc9IjEiIENFTExQQURESU5HPSI2Ij4NCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+SUQ8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5JRDI8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5BPC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj5hMTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj5hMTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj5hMTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij5hMjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij5hMjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij5hMjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj5hMzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj5hMzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj5hMzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICA8L1RBQkxFPj5dOw0KICAgICAgDQogICAgICBzdWJncmFwaCB7DQogICAgICByYW5rID0gc2FtZTsgdGFibGUxOyB0YWJsZTI7DQogICAgICB9DQogICAgICANCiAgICAgIA0KICAgICAgbGFiZWxsb2M9InQiOw0KICAgICAgZm9udG5hbWU9IkNvdXJpZXIiOw0KICAgICAgbGFiZWw9IndpZGUgIHZzICBsb25nIjsNCiAgICAgIH0NCiAgICAgICcpDQogIA0KDQpgYGANCg0KV2UgY3VycmVudGx5IGhhdmUgYSBmZXcgSUQgdHlwZSB2YXJpYWJsZSAoaS5lLiB0aGV5IGFyZSBub3Qgb2JzZXJ2YXRpb25zOyBgeWVhcmAsYG1vbnRoYCxgZGF5YCxgdGltZWAsYGRlcHRoYCxgc2l0ZWAsYGxvbmdgLGBsYXRgKSBhbmQgYW4gb2JzZXJ2YXRpb25hbCB2YXJpYWJsZSBmb3IgZWFjaCBzcGVjaWVzLg0KDQpgYGB7cn0NCm5hbWVzKGxhcnZhbEFidW5kYW5jZSkNCmxhcnZhbEFidW5kYW5jZUxvbmcgPC0gZ2F0aGVyKGxhcnZhbEFidW5kYW5jZSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb2xkIGRhdGEgZnJhbWUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9ICJzY2llbnRpZmljTmFtZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB3aGF0IHRoZSBjb2x1bW4gb2Ygb2xkIGNvbHVtbiBuYW1lcyB3aWxsIGJlIGNhbGxlZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAibGFydmFsQWJ1bmRhbmNlSW5kUGVybTMiLCAgICAgICAgICAgICAgICAjIHdoYXQgdGhlIGdhdGhlcmVkIG51bWJlcnMgd2lsbCBiZSBjYWxsZWQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFzdHlyaXMubHVuYXRhOkNyYW5nb24uc2VwdGVtc3Bpbm9zYSkgICAgICAgICAgICAgIyB3aGljaCBjb2x1bW5zICB0byBnYXRoZXINCg0KIyBhbHRlcm5hdGl2ZWx5LCBJIGNvdWxkIG9mIHNwZWNpZmllZCB3aGljaCBjb2x1bW5zIG5vdCB0byBpbmNsdWRlIGluc3RlYWQNCmxhcnZhbEFidW5kYW5jZUxvbmcgPC0gZ2F0aGVyKGxhcnZhbEFidW5kYW5jZSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb2xkIGRhdGEgZnJhbWUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9ICJzY2llbnRpZmljTmFtZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB3aGF0IHRoZSBjb2x1bW4gb2Ygb2xkIGNvbHVtbiBuYW1lcyB3aWxsIGJlIGNhbGxlZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAibGFydmFsQWJ1bmRhbmNlSW5kUGVybTMiLCAgICAgICAgICAgICAgICAjIHdoYXQgdGhlIGdhdGhlcmVkIG51bWJlcnMgd2lsbCBiZSBjYWxsZWQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC15ZWFyLC1tb250aCwtZGF5LC10aW1lLC1kZXB0aCwtbG9jYXRpb25JRCwtZGVjaW1hbExvbmdpdHVkZSwtZGVjaW1hbExhdGl0dWRlKSAgIyB3aGljaCBjb2x1bW5zIG5vdCB0byBnYXRoZXINCg0KaGVhZChsYXJ2YWxBYnVuZGFuY2VMb25nKSAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KYGBgDQoNCldlIGNhbiBhbHNvIGNsZWFuIHVwIGFuZC9vciBzZXBhcmF0ZSB0aGUgYHNjaWVudGlmaWNOYW1lYCBjb2x1bW4NCg0KYGBge3J9DQojIGZpcnN0IGxldCdzIHN3YXAgbXVsdGlwbGUgc3BlY2llcyANCmxhcnZhbEFidW5kYW5jZUxvbmcgPC0gc2VwYXJhdGUobGFydmFsQWJ1bmRhbmNlTG9uZywgICAgICAgICAgICNvbGQgZGF0YSBmcmFtZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBzY2llbnRpZmljTmFtZSwgICAgICAgICAgIyBjb2x1bW4gdG8gYmUgc2VwZXJhdGVkDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludG8gPSBjKCJnZW51cyIsInNwZWNpZXMiKSwgICAjIHRvIGJlIHNlcGVyYXRlZCBpbnRvDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhID0gIm1lcmdlIiwgICAgICAgICAgICAgICAjIGlmIHRoZXJlIGFyZSB0b28gbWFueSAiLiIsIHdoYXQgdG8gZG8gd2l0aCB0aGUgZXh0cmEgY29sdW1uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZSA9IEZBTFNFLCAgICAgICAgICAgICAgICAjIHdoZXRoZXIgdG8gZGVsZXRlIHRoZSBzZXBlcmF0ZWQgY29sdW1uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcXC4iKSAgICAgICAgICAgICAgICAgICAjIHJlZ2V4IGZvciAiLiIsIGJ1dCBuZWVkIGVzY2FwZSBjaGFyYWN0ZXIgIlxcIiBzaW5jZSAuIGlzIGEgd2lsZGNhcmQNCg0KIyBMZXQncyBjaGVjayBvdXQgb3VyIHdvcmsNCmhlYWQobGFydmFsQWJ1bmRhbmNlTG9uZykNCg0KIyBnZW51cyBhbmQgc3BlY2llcyBkb24ndCBsb29rIHRlcnJpYmxlLCBidXQgc2NpZW50aWZpY05hbWUgc3RpbGwgaGFzIHRoYXQgcGVza3kgcGVyaW9kDQojIHdlIGNvdWxkIHVzZSBnc3ViIGFnYWluLCBidXQgdGhlIHJlZ2V4IGlzIHRyaWNreSBzaW5jZSB0aGVyZSBhcmUgc3BwLiBmb3Igc3BlY2llcw0KIyBwYXN0aW5nIHRoZSBnZW51cyBhbmQgc3BlY2llcyB0b2dldGhlciB3aWxsIGJlIG11Y2ggZWFzaWVyDQpsYXJ2YWxBYnVuZGFuY2VMb25nJHNjaWVudGlmaWNOYW1lIDwtIHBhc3RlKGxhcnZhbEFidW5kYW5jZUxvbmckZ2VudXMsbGFydmFsQWJ1bmRhbmNlTG9uZyRzcGVjaWVzLHNlcCA9ICIgIikNCg0KIyBhbHNvLCBzcHAuIGlzIG5vdCBhIHNwZWNpZXMgZGVzaWduYXRpb24sIGxldCdzIGdldCByaWQgb2YgdGhlbQ0KbGFydmFsQWJ1bmRhbmNlTG9uZyRzcGVjaWVzW2xhcnZhbEFidW5kYW5jZUxvbmckc3BlY2llcz09InNwcC4iXSA8LSBOQQ0KDQojIExldCdzIGNoZWNrIG91dCBvdXIgd29yayBhZ2Fpbg0KaGVhZChsYXJ2YWxBYnVuZGFuY2VMb25nKQ0KYGBgDQoNCg0KIyMgVGhlIGBkcGx5cmAgcGFja2FnZQ0KDQpNYW5pcHVsYXRpb24gb2YgZGF0YWZyYW1lcyBtZWFucyBtYW55IHRoaW5ncyB0byBtYW55IHJlc2VhcmNoZXJzLCB3ZSBvZnRlbg0Kc2VsZWN0IGNlcnRhaW4gb2JzZXJ2YXRpb25zIChyb3dzKSBvciB2YXJpYWJsZXMgKGNvbHVtbnMpLCB3ZSBvZnRlbiBncm91cCB0aGUNCmRhdGEgYnkgYSBjZXJ0YWluIHZhcmlhYmxlKHMpLCBvciB3ZSBldmVuIGNhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MuIFdlIGNhbg0KZG8gdGhlc2Ugb3BlcmF0aW9ucyB1c2luZyB0aGUgbm9ybWFsIGJhc2UgUiBvcGVyYXRpb25zOg0KDQpgYGB7cn0NCm1lYW4obGFydmFsQWJ1bmRhbmNlTG9uZ1tsYXJ2YWxBYnVuZGFuY2VMb25nJHNjaWVudGlmaWNOYW1lID09ICJBc3R5cmlzIGx1bmF0YSIsICJsYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyJdKQ0KbWVhbihsYXJ2YWxBYnVuZGFuY2VMb25nW2xhcnZhbEFidW5kYW5jZUxvbmckc2NpZW50aWZpY05hbWUgPT0gIkJpdHRpb2x1bSBhbHRlcm5hdHVtIiwgImxhcnZhbEFidW5kYW5jZUluZFBlcm0zIl0pDQptZWFuKGxhcnZhbEFidW5kYW5jZUxvbmdbbGFydmFsQWJ1bmRhbmNlTG9uZyRzY2llbnRpZmljTmFtZSA9PSAiQXJyaG9nZXMgb2NjaWRlbnRhbGlzIiwgImxhcnZhbEFidW5kYW5jZUluZFBlcm0zIl0pDQpgYGANCg0KQnV0IHRoaXMgaXNuJ3QgdmVyeSAqbmljZSogYmVjYXVzZSB0aGVyZSBpcyBhIGZhaXIgYml0IG9mIHJlcGV0aXRpb24uIFJlcGVhdGluZw0KeW91cnNlbGYgd2lsbCBjb3N0IHlvdSB0aW1lLCBib3RoIG5vdyBhbmQgbGF0ZXIsIGFuZCBwb3RlbnRpYWxseSBpbnRyb2R1Y2Ugc29tZQ0KbmFzdHkgYnVncy4NCg0KTHVja2lseSwgdGhlIFtgZHBseXJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZHBseXIvZHBseXIucGRmKQ0KcGFja2FnZSBwcm92aWRlcyBhIG51bWJlciBvZiB2ZXJ5IHVzZWZ1bCBmdW5jdGlvbnMgZm9yIG1hbmlwdWxhdGluZyBkYXRhZnJhbWVzDQppbiBhIHdheSB0aGF0IHdpbGwgcmVkdWNlIHRoZSBhYm92ZSByZXBldGl0aW9uLCByZWR1Y2UgdGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZw0KZXJyb3JzLCBhbmQgcHJvYmFibHkgZXZlbiBzYXZlIHlvdSBzb21lIHR5cGluZy4gQXMgYW4gYWRkZWQgYm9udXMsIHlvdSBtaWdodA0KZXZlbiBmaW5kIHRoZSBgZHBseXJgIGdyYW1tYXIgZWFzaWVyIHRvIHJlYWQuDQoNCkhlcmUgd2UncmUgZ29pbmcgdG8gY292ZXIgNSBvZiB0aGUgbW9zdCBjb21tb25seSB1c2VkIGZ1bmN0aW9ucyBhcyB3ZWxsIGFzIHVzaW5nDQpwaXBlcyAoYCU+JWApIHRvIGNvbWJpbmUgdGhlbS4NCg0KMS4gYHNlbGVjdCgpYA0KMi4gYGZpbHRlcigpYA0KMy4gYGdyb3VwX2J5KClgDQo0LiBgc3VtbWFyaXplKClgDQo1LiBgbXV0YXRlKClgDQoNCiMjIyBVc2luZyBgc2VsZWN0KClgDQoNCklmLCBmb3IgZXhhbXBsZSwgd2Ugd2FudGVkIHRvIG1vdmUgZm9yd2FyZCB3aXRoIG9ubHkgYSBmZXcgb2YgdGhlIHZhcmlhYmxlcyBpbg0Kb3VyIGRhdGFmcmFtZSB3ZSBjb3VsZCB1c2UgdGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24uIFRoaXMgd2lsbCBrZWVwIG9ubHkgdGhlDQp2YXJpYWJsZXMgeW91IHNlbGVjdC4NCg0KDQpgYGB7ciBzZWxlY3QscmVzdWx0cz0nYXNpcycsZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdyVml6KCdkaWdyYXBoIGh0bWwgew0KICAgICAgdGFibGUxIFtzaGFwZT1ub25lLCBtYXJnaW49MCxsYWJlbD08DQogICAgICA8VEFCTEUgQk9SREVSPSIwIiBDRUxMQk9SREVSPSIwIiBDRUxMU1BBQ0lORz0iMSIgQ0VMTFBBRERJTkc9IjYiPg0KICAgICAgPFRSPg0KICAgICAgPFREIFBPUlQ9ImYwIiBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+YTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCIgQ0VMTFBBRERJTkc9IjAiPmI8L1REPg0KICAgICAgPFREIFBPUlQ9ImYxIiBCR0NPTE9SPSIjMDA5OTk5IiBDRUxMUEFERElORz0iMCI+YzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCIgQ0VMTFBBRERJTkc9IjAiPmQ8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlMiBbc2hhcGU9bm9uZSwgbWFyZ2luPTAsIGxhYmVsPTwNCiAgICAgIDxUQUJMRSBCT1JERVI9IjAiIENFTExCT1JERVI9IjAiIENFTExTUEFDSU5HPSIxIiBDRUxMUEFERElORz0iNiI+DQogICAgICA8VFI+DQogICAgICA8VEQgUE9SVD0iZjAiIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5hPC9URD4NCiAgICAgIDxURCBQT1JUPSJmMSIgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmM8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlMTpmMSAtPiB0YWJsZTI6ZjEgDQogICAgICB0YWJsZTE6ZjAgLT4gdGFibGUyOmYwDQogICAgICANCiAgICAgIHN1YmdyYXBoIHsNCiAgICAgIHJhbmsgPSBzYW1lOyB0YWJsZTE7IHRhYmxlMjsNCiAgICAgIH0NCiAgICAgIA0KICAgICAgbGFiZWxsb2M9InQiOw0KICAgICAgZm9udG5hbWU9IkNvdXJpZXIiOw0KICAgICAgbGFiZWw9InNlbGVjdChkYXRhLmZyYW1lLGEsYykiOw0KICAgICAgfQ0KICAgICAgJykNCiAgDQpgYGANCg0KYGBge3J9DQp5ZWFyQW5kU3BlY2llcyA8LSBzZWxlY3QobGFydmFsQWJ1bmRhbmNlTG9uZyx5ZWFyLHNwZWNpZXMsZ2VudXMsbGFydmFsQWJ1bmRhbmNlSW5kUGVybTMpDQpgYGANCg0KDQpJZiB3ZSBvcGVuIHVwIGB5ZWFyQW5kU3BlY2llc2Agd2UnbGwgc2VlIHRoYXQgaXQgb25seSBjb250YWlucyB0aGUgeWVhciwgYW5kIHRoZSBsYXJ2YWwgYWJ1bmRhbmNlIGZvciBlYWNoIHNwZWNpZXMuIA0KDQpBYm92ZSB3ZSB1c2VkICdub3JtYWwnIGdyYW1tYXIsIGJ1dCB0aGUgc3RyZW5ndGhzIG9mIGBkcGx5cmAgbGllIGluIGNvbWJpbmluZyBzZXZlcmFsIGZ1bmN0aW9ucyB1c2luZyBwaXBlcyAoYCU+JWApLiBTaW5jZSB0aGUgcGlwZXMgZ3JhbW1hciBpcyB1bmxpa2UgYW55dGhpbmcgd2UndmUgc2VlbiBpbiBSIGJlZm9yZSwgbGV0J3MgcmVwZWF0IHdoYXQgd2UndmUgZG9uZSBhYm92ZSB1c2luZyBwaXBlcy4NCg0KYGBge3J9DQp5ZWFyVGF4b25BYnVuIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lIHNlbGVjdCh5ZWFyLHNwZWNpZXMsZ2VudXMsbGFydmFsQWJ1bmRhbmNlSW5kUGVybTMpDQpgYGANCg0KVG8gaGVscCB5b3UgdW5kZXJzdGFuZCB3aHkgd2Ugd3JvdGUgdGhhdCBpbiB0aGF0IHdheSwgbGV0J3Mgd2FsayB0aHJvdWdoIGl0IHN0ZXAgYnkgc3RlcC4gSW4gYm90aCBjYXNlcywgdGhlIGxlZnQgc2lkZSBvZiBgPC1gIGlzIGB5ZWFyVGF4b25BYnVuYC4gRmlyc3Qgd2Ugc3VtbW9uIHRoZSBgbGFydmFsQWJ1bmRhbmNlTG9uZ2AgZGF0YWZyYW1lIGFuZCBwYXNzIGl0IG9uLCB1c2luZyB0aGUgcGlwZSBzeW1ib2wgYCU+JWAsIHRvIHRoZSBuZXh0IHN0ZXAsIHdoaWNoIGlzIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uLiBJbiB0aGlzIGNhc2Ugd2UgZG9uJ3Qgc3BlY2lmeSB3aGljaCBgLmRhdGFgIGFyZ3VtZW50IHdlIHVzZSBpbiB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiBzaW5jZSBpbiBnZXRzIHRoYXQgZnJvbSB0aGUgcHJldmlvdXMgcGlwZS4gRXNzZW50aWFsbHksIHRoZSBvdXRwdXQgb2Ygd2hhdCBpcyB3cml0dGVuIGJlZm9yZSB0aGUgcGlwZSBiZWNvbWVzIHRoZSBmaXJzdCBhcmd1bWVudCwgaW4gdGhpcyBjYXNlIGAuZGF0YWAsIG9mIHRoZSBmdW5jdGlvbiBhZnRlciB0aGUgcGlwZS4gKipGdW4gRmFjdCoqOiBUaGVyZSBpcyBhIGNoYW5jZSB5b3UgaGF2ZSBlbmNvdW50ZXJlZCBwaXBlcyBiZWZvcmUgaW4gdGhlIHNoZWxsLiBJbiBSLCBhIHBpcGUgc3ltYm9sIGlzIGAlPiVgIHdoaWxlIGluIHRoZSBzaGVsbCBpdCBpcyBgfGAgYnV0IHRoZSBjb25jZXB0IGlzIHRoZSBzYW1lIQ0KDQo+KipQcm8tdGlwKioNCj4NCj4gRGlkIHlvdSBnZXQgYW4gZXJyb3Igd2hpbGUgcnVubmluZyB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbj8gU29tZXRoaW5nIGxpa2U6DQo+IGBFcnJvciBpbiAoZnVuY3Rpb24gKGNsYXNzZXMsIGZkZWYsIG10YWJsZSkgIDogYA0KPiAgYHVuYWJsZSB0byBmaW5kIGFuIGluaGVyaXRlZCBtZXRob2QgZm9yIGZ1bmN0aW9uIOKAmHNlbGVjdOKAmSBmb3Igc2lnbmF0dXJlIOKAmCJkYXRhLmZyYW1lIuKAmWANCj4gVGhhdCdzIGJlY2F1c2UgbWFueSBwYWNrYWdlcyBoYXZlIGEgYHNlbGVjdCgpYCBmdW5jdGlvbjsgSW4gdGhpcyBjYXNlLCBpdCdzIHRoZSBgcmFzdGVyYCBwYWNrYWdlIGNhdXNpbmcgcHJvYmxlbXMuDQo+IEl0IGlzIHBvc3NpYmxlIHRvIGdldCBhcm91bmQgdGhhdCBwcm9ibGVtIHRoZSAnaGFja3knIHdheSBieSBsb2FkaW5nIGB0aWR5dmVyc2VgIGFmdGVyIGFsbCB0aGUgb3RoZXIgcGFja2FnZXMgc28gdGhhdCB0aGUgYGRwbHlyYCAod2hpY2ggaXMgaW4gdGhlIGB0aWR5dmVyc2VgKSB2ZXJzaW9uIG9mIGBzZWxlY3RgIHNvcnQgb2Ygb3ZlcndyaXRlcyB0aGUgb25lIGxvYWRlZCBmcm9tIGByYXN0ZXJgLg0KPg0KPiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIGRlc2lnbmF0ZSB3aGljaCBwYWNrYWdlIHVzaW5nIHRoZSBgOjpgIChlLmcuIGBwYWNrYWdlOjpmdW5jdGlvbigpYCkgd2hlbiBpdCdzIG5vdCBjbGVhci4gDQo+IGB5ZWFyVGF4b25BYnVuIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lIGRwbHlyOjpzZWxlY3QoeWVhcixzcGVjaWVzLGdlbnVzLGxhcnZhbEFidW5kYW5jZUluZFBlcm0zKWANCg0KIyMjIFVzaW5nIGBmaWx0ZXIoKWANCg0KSWYgd2Ugbm93IHdhbnRlZCB0byBtb3ZlIGZvcndhcmQgd2l0aCB0aGUgYWJvdmUsIGJ1dCBvbmx5IHdpdGggc3BlY2llcyBmb3Igd2hpY2ggd2UgaGF2ZSBhIHZhbGlkIGBnZW51c2AgYW5kIGBzcGVjaWVzYCwgd2UgY2FuIGNvbWJpbmUgYHNlbGVjdGAgYW5kIGBmaWx0ZXJgDQoNCmBgYHtyfQ0KeWVhclRheG9uQWJ1blZhbGlkIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lDQogICAgc2VsZWN0KHllYXIsc3BlY2llcyxnZW51cyxsYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMykgJT4lIA0KICAgIGZpbHRlcihnZW51cyE9Ik90aGVyIikgJT4lIA0KICAgIGZpbHRlcighaXMubmEoc3BlY2llcykpDQoNCiMgd2UgY2FuIGFsc28gc3RyaW5nIHRoZSB0d28gZmlsdGVyIG9wZXJhdGlvbnMgaW50byBvbmUgdXNpbmcgdGhlIGFuZCBzeW1ib2wgJyYnDQoNCnllYXJUYXhvbkFidW5WYWxpZCA8LSBsYXJ2YWxBYnVuZGFuY2VMb25nICU+JQ0KICAgIHNlbGVjdCh5ZWFyLHNwZWNpZXMsZ2VudXMsbGFydmFsQWJ1bmRhbmNlSW5kUGVybTMpICU+JSANCiAgICBmaWx0ZXIoZ2VudXMhPSJPdGhlciIgJiAhaXMubmEoc3BlY2llcykpDQpgYGANCg0KPiAqKiBDaGFsbGVuZ2UgMSAqKg0KPg0KPiBXcml0ZSBhIHNpbmdsZSBjb21tYW5kICh3aGljaCBjYW4gc3BhbiBtdWx0aXBsZSBsaW5lcyBhbmQgaW5jbHVkZXMgcGlwZXMpIHRoYXQgd2lsbCBwcm9kdWNlIGEgZGF0YWZyYW1lIHRoYXQgaGFzIHRoZSB2YWx1ZXMgZm9yIGBsYXJ2YWxBYnVuZGFuY2VJbmRQZXJtM2AgZm9yIGFsbCBzcGVjaWVzIGF0IGBsb2NhdGlvbklEYCA1LCBidXQgbm90IGZvciBvdGhlciBzaXRlcy4gSSBhbHNvIHdhbnQgbG9jYXRpb25JRCwgYW5kIGxhdC9sb25nIHJlbW92ZWQgZnJvbSB0aGUgb3V0cHV0LiBIb3cgbWFueSByb3dzIGRvZXMgeW91ciBkYXRhZnJhbWUgaGF2ZSBhbmQgd2h5Pw0KPiBbU29sdXRpb25dKGh0dHBzOi8vcmVtaS1kYWlnbGUuZ2l0aHViLmlvLzIwMTctQ0hPTmUtRGF0YS9kYXRhdml6Lm5iLmh0bWwjY2hhbGxlbmdlX2Fuc3dlcnMpDQoNCg0KIyMjIFVzaW5nIGBncm91cF9ieSgpYA0KDQpOb3csIHdlIHdlcmUgc3VwcG9zZWQgdG8gYmUgcmVkdWNpbmcgdGhlIGVycm9yIHByb25lIHJlcGV0aXRpdmVuZXNzIG9mIHdoYXQgY2FuDQpiZSBkb25lIHdpdGggYmFzZSBSLCBidXQgdXAgdG8gbm93IHdlIGhhdmVuJ3QgZG9uZSB0aGF0IHNpbmNlIHdlIHdvdWxkIGhhdmUgdG8NCnJlcGVhdCB0aGUgYWJvdmUgZm9yIGVhY2ggc3BlY2llcy4gSW5zdGVhZCBvZiBgZmlsdGVyKClgLCB3aGljaCB3aWxsIG9ubHkgcGFzcw0Kb2JzZXJ2YXRpb25zIHRoYXQgbWVldCB5b3VyIGNyaXRlcmlhIChpbiB0aGUgYWJvdmU6IGBsb2NhdGlvbklEPT01YCksIHdlDQpjYW4gdXNlIGBncm91cF9ieSgpYCwgd2hpY2ggd2lsbCBlc3NlbnRpYWxseSB1c2UgZXZlcnkgdW5pcXVlIGNyaXRlcmlhIHRoYXQgeW91DQpjb3VsZCBoYXZlIHVzZWQgaW4gZmlsdGVyLg0KDQpgYGB7cn0NCnN0cihsYXJ2YWxBYnVuZGFuY2VMb25nKQ0KDQpzdHIobGFydmFsQWJ1bmRhbmNlTG9uZyAlPiUgZ3JvdXBfYnkobG9jYXRpb25JRCkpDQpgYGANCllvdSB3aWxsIG5vdGljZSB0aGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFmcmFtZSB3aGVyZSB3ZSB1c2VkIGBncm91cF9ieSgpYA0KKGBncm91cGVkX2RmYCkgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBvcmlnaW5hbCBgbGFydmFsQWJ1bmRhbmNlTG9uZ2AgKGBkYXRhLmZyYW1lYCkuIEENCmBncm91cGVkX2RmYCBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIGBsaXN0YCB3aGVyZSBlYWNoIGl0ZW0gaW4gdGhlIGBsaXN0YGlzIGENCmBkYXRhLmZyYW1lYCB3aGljaCBjb250YWlucyBvbmx5IHRoZSByb3dzIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgYSBwYXJ0aWN1bGFyDQp2YWx1ZSBgbG9jYXRpb25JRGAgKGF0IGxlYXN0IGluIHRoZSBleGFtcGxlIGFib3ZlKS4NCg0KYGBge3IgZ3JvdXBfYnkscmVzdWx0cz0nYXNpcycsZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdyVml6KCdkaWdyYXBoIGh0bWwgew0KICAgICAgdGFibGUxIFtzaGFwZT1ub25lLCBtYXJnaW49MCxsYWJlbD08DQogICAgICA8VEFCTEUgQk9SREVSPSIwIiBDRUxMQk9SREVSPSIwIiBDRUxMU1BBQ0lORz0iMSIgQ0VMTFBBRERJTkc9IjYiPg0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5hPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIiBDRUxMUEFERElORz0iMCI+YjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiIENFTExQQURESU5HPSIwIj5kPC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYwIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYxIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYyIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICA8L1RBQkxFPj5dOw0KICAgICAgdGFibGUyIFtzaGFwZT1ub25lLCBtYXJnaW49MCxsYWJlbD08DQogICAgICA8VEFCTEUgQk9SREVSPSIwIiBDRUxMQk9SREVSPSIwIiBDRUxMU1BBQ0lORz0iMSIgQ0VMTFBBRERJTkc9IjYiPg0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5hPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIiBDRUxMUEFERElORz0iMCI+YjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiIENFTExQQURESU5HPSIwIj5kPC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMCIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICB0YWJsZTMgW3NoYXBlPW5vbmUsIG1hcmdpbj0wLGxhYmVsPTwNCiAgICAgIDxUQUJMRSBCT1JERVI9IjAiIENFTExCT1JERVI9IjAiIENFTExTUEFDSU5HPSIxIiBDRUxMUEFERElORz0iNiI+DQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPmE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiIENFTExQQURESU5HPSIwIj5iPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5IiBDRUxMUEFERElORz0iMCI+YzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCIgQ0VMTFBBRERJTkc9IjAiPmQ8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIFBPUlQ9ImYxIiBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjI8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICB0YWJsZTQgW3NoYXBlPW5vbmUsIG1hcmdpbj0wLGxhYmVsPTwNCiAgICAgIDxUQUJMRSBCT1JERVI9IjAiIENFTExCT1JERVI9IjAiIENFTExTUEFDSU5HPSIxIiBDRUxMUEFERElORz0iNiI+DQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPmE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiIENFTExQQURESU5HPSIwIj5iPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5IiBDRUxMUEFERElORz0iMCI+YzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCIgQ0VMTFBBRERJTkc9IjAiPmQ8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIFBPUlQ9ImYyIiBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIA0KICAgICAgDQogICAgICB0YWJsZTE6ZjAgLT4gdGFibGUyOmYwIA0KICAgICAgdGFibGUxOmYxIC0+IHRhYmxlMzpmMQ0KICAgICAgdGFibGUxOmYyIC0+IHRhYmxlNDpmMg0KICAgICAgDQogICAgICBzdWJncmFwaCB7DQogICAgICByYW5rID0gc2FtZTsgdGFibGUxOyB0YWJsZTI7IHRhYmxlMyA7dGFibGU0Ow0KICAgICAgfQ0KICAgICAgc3ViZ3JhcGggew0KICAgICAgdGFibGUyDQogICAgICB0YWJsZTMNCiAgICAgIHRhYmxlNA0KICAgICAgfQ0KICAgICAgDQogICAgICBsYWJlbGxvYz0idCI7DQogICAgICBmb250bmFtZT0iQ291cmllciI7DQogICAgICBsYWJlbD0iZGF0YS5mcmFtZSAlPiUgZ3JvdXBfYnkoYSkiOw0KICAgICAgfQ0KICAgICAgJykNCg0KYGBgDQoNCiMjIyBVc2luZyBgc3VtbWFyaXplKClgDQoNClRoZSBhYm92ZSB3YXMgYSBiaXQgb24gdGhlIHVuZXZlbnRmdWwgc2lkZSBiZWNhdXNlIGBncm91cF9ieSgpYCBtdWNoIG1vcmUNCmV4Y2l0aW5nIGluIGNvbmp1bmN0aW9uIHdpdGggYHN1bW1hcml6ZSgpYC4gVGhpcyB3aWxsIGFsbG93IHVzZSB0byBjcmVhdGUgbmV3DQp2YXJpYWJsZShzKSBieSB1c2luZyBmdW5jdGlvbnMgdGhhdCByZXBlYXQgZm9yIGVhY2ggb2YgdGhlIGxvY2F0aW9uSUQtc3BlY2lmaWMNCmRhdGEgZnJhbWVzLiBUaGF0IGlzIHRvIHNheSwgdXNpbmcgdGhlIGBncm91cF9ieSgpYCBmdW5jdGlvbiwgd2Ugc3BsaXQgb3VyDQpvcmlnaW5hbCBkYXRhZnJhbWUgaW50byBtdWx0aXBsZSBwaWVjZXMsIHRoZW4gd2UgY2FuIHJ1biBmdW5jdGlvbnMNCihlLmcuIGBtZWFuKClgIG9yIGBzZCgpYCkgd2l0aGluIGBzdW1tYXJpemUoKWAuDQoNCmBgYHtyfQ0KSW5kUGVybTNfYnlMb2NhdGlvbklEIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lDQogICAgZ3JvdXBfYnkobG9jYXRpb25JRCkgJT4lDQogICAgc3VtbWFyaXplKG1lYW5fbGFydmFsQWJ1bmRhbmNlSW5kUGVybTMgPSBtZWFuKGxhcnZhbEFidW5kYW5jZUluZFBlcm0zKSkNCmBgYA0KDQpgYGB7ciBzdW1tYXJpemUscmVzdWx0cz0nYXNpcycsZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdyVml6KCdkaWdyYXBoIGh0bWwgew0KICAgICAgdGFibGUxIFtzaGFwZT1ub25lLCBtYXJnaW49MCxsYWJlbD08DQogICAgICA8VEFCTEUgQk9SREVSPSIwIiBDRUxMQk9SREVSPSIwIiBDRUxMU1BBQ0lORz0iMSIgQ0VMTFBBRERJTkc9IjYiPg0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5hPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIiBDRUxMUEFERElORz0iMCI+YjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiIENFTExQQURESU5HPSIwIj5kPC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYwIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYxIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIFBPUlQ9ImYyIiBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICA8L1RBQkxFPj5dOw0KICAgICAgDQogICAgICB0YWJsZTIgW3NoYXBlPW5vbmUsIG1hcmdpbj0wLGxhYmVsPTwNCiAgICAgIDxUQUJMRSBCT1JERVI9IjAiIENFTExCT1JERVI9IjAiIENFTExTUEFDSU5HPSIxIiBDRUxMUEFERElORz0iNiI+DQogICAgICA8VFI+DQogICAgICA8VEQgUE9SVD0iZjAiIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj5hPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIiBDRUxMUEFERElORz0iMCI+YjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSIgQ0VMTFBBRERJTkc9IjAiPmM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiIENFTExQQURESU5HPSIwIj5kPC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5Ij48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMENDMDAiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgUE9SVD0iZjMiIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlMyBbc2hhcGU9bm9uZSwgbWFyZ2luPTAsbGFiZWw9PA0KICAgICAgPFRBQkxFIEJPUkRFUj0iMCIgQ0VMTEJPUkRFUj0iMCIgQ0VMTFNQQUNJTkc9IjEiIENFTExQQURESU5HPSI2Ij4NCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMSIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPmE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiIENFTExQQURESU5HPSIwIj5iPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5IiBDRUxMUEFERElORz0iMCI+YzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCIgQ0VMTFBBRERJTkc9IjAiPmQ8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4yPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMyIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjI8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlNCBbc2hhcGU9bm9uZSwgbWFyZ2luPTAsbGFiZWw9PA0KICAgICAgPFRBQkxFIEJPUkRFUj0iMCIgQ0VMTEJPUkRFUj0iMCIgQ0VMTFNQQUNJTkc9IjEiIENFTExQQURESU5HPSI2Ij4NCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMiIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPmE8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiIENFTExQQURESU5HPSIwIj5iPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDA5OTk5IiBDRUxMUEFERElORz0iMCI+YzwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCIgQ0VMTFBBRERJTkc9IjAiPmQ8L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4zPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiMwMDk5OTkiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwQ0MwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMyIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iIzAwOTk5OSI+PC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjMDBDQzAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIDwvVEFCTEU+Pl07DQogICAgICANCiAgICAgIHRhYmxlNSBbc2hhcGU9bm9uZSwgbWFyZ2luPTAsbGFiZWw9PA0KICAgICAgPFRBQkxFIEJPUkRFUj0iMCIgQ0VMTEJPUkRFUj0iMCIgQ0VMTFNQQUNJTkc9IjEiIENFTExQQURESU5HPSI2Ij4NCiAgICAgIDxUUj4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+YTwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCIgQ0VMTFBBRERJTkc9IjAiPm1lYW5fYjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8VFI+DQogICAgICA8VEQgUE9SVD0iZjAiIEJHQ09MT1I9IiNGRjAwMDAiIENFTExQQURESU5HPSIwIj4xPC9URD4NCiAgICAgIDxURCBCR0NPTE9SPSIjRkY3NDAwIj48L1REPg0KICAgICAgPC9UUj4NCiAgICAgIA0KICAgICAgPFRSPg0KICAgICAgPFREIFBPUlQ9ImYxIiBCR0NPTE9SPSIjRkYwMDAwIiBDRUxMUEFERElORz0iMCI+MjwvVEQ+DQogICAgICA8VEQgQkdDT0xPUj0iI0ZGNzQwMCI+PC9URD4NCiAgICAgIDwvVFI+DQogICAgICANCiAgICAgIDxUUj4NCiAgICAgIDxURCBQT1JUPSJmMiIgQkdDT0xPUj0iI0ZGMDAwMCIgQ0VMTFBBRERJTkc9IjAiPjM8L1REPg0KICAgICAgPFREIEJHQ09MT1I9IiNGRjc0MDAiPjwvVEQ+DQogICAgICA8L1RSPg0KICAgICAgDQogICAgICA8L1RBQkxFPj5dOw0KICAgICAgDQogICAgICANCiAgICAgIHRhYmxlMTpmMCAtPiB0YWJsZTI6ZjAgDQogICAgICB0YWJsZTE6ZjEgLT4gdGFibGUzOmYxDQogICAgICB0YWJsZTE6ZjIgLT4gdGFibGU0OmYyDQogICAgICB0YWJsZTI6ZjMgLT4gdGFibGU1OmYwIA0KICAgICAgdGFibGUzOmYzIC0+IHRhYmxlNTpmMQ0KICAgICAgdGFibGU0OmYzIC0+IHRhYmxlNTpmMg0KICAgICAgc3ViZ3JhcGggew0KICAgICAgcmFuayA9IHNhbWU7IHRhYmxlMTsgdGFibGUyOyB0YWJsZTMgOyB0YWJsZTQ7DQogICAgICB9DQogICAgICBzdWJncmFwaCB7DQogICAgICB0YWJsZTINCiAgICAgIHRhYmxlMw0KICAgICAgdGFibGU0DQogICAgICB9DQogICAgICANCiAgICAgIGxhYmVsbG9jPSJ0IjsNCiAgICAgIGZvbnRuYW1lPSJDb3VyaWVyIjsNCiAgICAgIGxhYmVsPSJkYXRhLmZyYW1lICU+JVxcbFx0Z3JvdXBfYnkoYSkgJT4lXFxsXHRzdW1tYXJpemUobWVhbl9iPW1lYW4oYikpXFxsIjsNCiAgICAgIH0NCiAgICAgICcpDQpgYGANCg0KVGhhdCBhbGxvd2VkIHVzIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBsYXJ2YWwgYWJ1bmRhbmNlIGZvciBlYWNoIGxvY2F0aW9uLCBidXQgaXQgZ2V0cw0Kc28gbXVjaCBiZXR0ZXIhDQoNCj4gKiogQ2hhbGxlbmdlIDIgKioNCj4NCj4gQ2FsY3VsYXRlIHRoZSBtZWFuIGxhcnZhbCBhYnVuZGFuY2UgcGVyIGxvY2F0aW9uSUQuIFdoaWNoIGhhcyB0aGUgaGlnaGVzdCBtZWFuIGxhcnZhbCBhYnVuZGFuY2UgYW5kIHdoaWNoIGhhcyB0aGUgbG93ZXN0IG1lYW4gbGFydmFsIGFidW5kYW5jZT8NCj4gW1NvbHV0aW9uXShodHRwczovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby8yMDE3LUNIT05lLURhdGEvZGF0YXZpei5uYi5odG1sI2NoYWxsZW5nZV9hbnN3ZXJzKQ0KDQoNClRoZSBmdW5jdGlvbiBgZ3JvdXBfYnkoKWAgYWxsb3dzIHVzIHRvIGdyb3VwIGJ5IG11bHRpcGxlIHZhcmlhYmxlcy4gTGV0J3MgZ3JvdXAgYnkgYGxvY2F0aW9uSURgIGFuZCBgc2NpZW50aWZpY05hbWVgLg0KDQoNCmBgYHtyfQ0KSW5kUGVybTNfYnlMb2NhdGlvbklEX2J5U3BwIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lDQogICAgZ3JvdXBfYnkobG9jYXRpb25JRCxzY2llbnRpZmljTmFtZSApICU+JQ0KICAgIHN1bW1hcml6ZShtZWFuX2xhcnZhbEFidW5kYW5jZUluZFBlcm0zID0gbWVhbihsYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMykpDQpgYGANCg0KVGhhdCBpcyBhbHJlYWR5IHF1aXRlIHBvd2VyZnVsLCBidXQgaXQgZ2V0cyBldmVuIGJldHRlciEgWW91J3JlIG5vdCBsaW1pdGVkIHRvIGRlZmluaW5nIDEgbmV3IHZhcmlhYmxlIGluIGBzdW1tYXJpemUoKWAuDQoNCmBgYHtyfQ0KSW5kUGVybTNfYnlMb2NhdGlvbklEX2J5U3BwIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lDQogICAgZ3JvdXBfYnkobG9jYXRpb25JRCxzY2llbnRpZmljTmFtZSApICU+JQ0KICAgIHN1bW1hcml6ZShtZWFuX2xhcnZhbEFidW5kYW5jZUluZFBlcm0zID0gbWVhbihsYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyksDQogICAgICAgICAgICAgIHNkX2xhcnZhbEFidW5kYW5jZUluZFBlcm0zID0gc2QobGFydmFsQWJ1bmRhbmNlSW5kUGVybTMpLA0KICAgICAgICAgICAgICBtZWFuX2RlcHRoID0gbWVhbihkZXB0aCksDQogICAgICAgICAgICAgIHNkX2RlcHRoPSBzZChkZXB0aCkpDQpgYGANCg0KIyMjIFVzaW5nIGBtdXRhdGUoKWANCg0KV2UgY2FuIGFsc28gY3JlYXRlIG5ldyB2YXJpYWJsZXMgcHJpb3IgdG8gKG9yIGV2ZW4gYWZ0ZXIpIHN1bW1hcml6aW5nIGluZm9ybWF0aW9uIHVzaW5nIGBtdXRhdGUoKWAuDQoNCmBgYHtyfQ0KSW5kUGVybTNfYnlMb2NhdGlvbklEX2J5U3BwIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lDQogICAgbXV0YXRlKGxhcnZhbEFidW5kYW5jZUluZFBlcmNtMz1sYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyoxMDAwMDAwKSAlPiUgDQogICAgZ3JvdXBfYnkobG9jYXRpb25JRCxzY2llbnRpZmljTmFtZSApICU+JQ0KICAgIHN1bW1hcml6ZShtZWFuX2xhcnZhbEFidW5kYW5jZUluZFBlcmNtMyA9IG1lYW4obGFydmFsQWJ1bmRhbmNlSW5kUGVyY20zKSwNCiAgICAgICAgICAgICAgc2RfbGFydmFsQWJ1bmRhbmNlSW5kUGVyY20zID0gc2QobGFydmFsQWJ1bmRhbmNlSW5kUGVyY20zKSwNCiAgICAgICAgICAgICAgbWVhbl9kZXB0aCA9IG1lYW4oZGVwdGgpLA0KICAgICAgICAgICAgICBzZF9kZXB0aD0gc2QoZGVwdGgpKQ0KYGBgDQoNCiMgUGxvdHRpbmcNClIgY29tZXMgd2l0aCBiYXNpYyBwbG90dGluZyBmdW5jdGlvbiwgdXN1YWxseSByZWZlcnJlZCB0byBhcyAnYmFzZSBwbG90JywgZS5nOiANCg0KYGBge3J9DQpwbG90KGxhcnZhbEFidW5kYW5jZSRNYXJnYXJpdGVzLnNwcC4sbGFydmFsQWJ1bmRhbmNlJERpYXBoYW5hLm1pbnV0YSkNCmBgYA0KDQpIb3dldmVyLCBnZW5lcmFsIGNvbmNlbnN1cyBzZWVtcyB0byBiZSB0aGF0IGBnZ3Bsb3QyYCAocGFydCBvZiB0aGUgdGlkeXZlcnNlKSBpcyB0aGUgd2F5IHRvIGdvIGZvciBwbG90dGluZy4gSSAoUmVtaSkgaGF2ZSBtaXhlZCBmZWVsaW5ncyBhYm91dCB0aGlzIHBhY2thZ2UgKG15IGFnZSBpcyBzaG93aW5nIGFnYWluKS4gSXQgZG9lcyBtYWtlIG1ha2luZyBxdWljayBwbG90cyBlYXNpZXIgYW5kIGlzIGNvbXBhdGlibGUgd2l0aCB0aGUgcmVzdCBvZiB0aGUgdGlkeXZlcnNlLCBidXQgdW50aWwgcmVjZW50bHksIGBnZ3Bsb3QyYCByZWFsbHkgc3RydWdnbGVkIHdpdGggYW55IHNwYXRpYWwgcGxvdHRpbmcgKHNlZSAnW1RoZSBjbGFzc2ljIGFuZCB0aGUgY3V0dGluZyBlZGdlOiBgc3BgIHZzIGBzZmBdKGh0dHBzOi8vcmVtaS1kYWlnbGUuZ2l0aHViLmlvLzIwMTctQ0hPTmUtRGF0YS9kYXRhdml6Lm5iLmh0bWwjdGhlX2NsYXNzaWNfYW5kX3RoZV9jdXR0aW5nX2VkZ2U6X3NwX3ZzX3NmKScpIHNvIEkgaGFkIHRvIGtub3cgdGhlIGJhc2UgcGxvdCBhbnl3YXkuIEkgc3RpbGwgdGhpbmsgYmFzZSBwbG90IGlzIG1vcmUgY3VzdG9taXplYWJsZSwgYnV0IGl0IG1heSBhbHNvIGJlIHRoYXQgSSBoYXZlIG5vdCBzdWZmaWNpZW50bHkgbWFzdGVyZWQgdGhlIGdncGxvdCB3b3JsZC4gV2UncmUgbW9zdGx5IGdvaW5nIHRvIGNvdmVyIGBnZ3Bsb3QyYCwgYnV0IGJlIGF3YXJlIHRoYXQgdGhlcmUgYXJlIG90aGVyIHBsb3R0aW5nIHBhcmFkaWdtcyBpbiBSIChlLmcuIGJhc2UsIGxhdHRpY2UsIGV0YykNCg0KIyMgVGhlIGBnZ3Bsb3QyYCBwYWNrYWdlDQoNCkEgc3lzdGVtIGZvciAnZGVjbGFyYXRpdmVseScgY3JlYXRpbmcgZ3JhcGhpY3MsIGJhc2VkIG9uICJbVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3NdKGh0dHA6Ly93d3cuc3ByaW5nZXIuY29tL2dwL2Jvb2svOTc4MDM4NzI0NTQ0NykiLiBZb3UgcHJvdmlkZSB0aGUgZGF0YSwgdGVsbCAnZ2dwbG90MicgaG93IHRvIG1hcCB2YXJpYWJsZXMgdG8gYWVzdGhldGljcywgd2hhdCBncmFwaGljYWwgcHJpbWl0aXZlcyB0byB1c2UsIGFuZCBpdCB0YWtlcyBjYXJlIG9mIHRoZSBkZXRhaWxzDQoNClNvIGZvciB0aGUgc2FtZSBwbG90IGFzIGFib3ZlLCBsZXQgbWUgd2FsayB5b3UgdGhyb3VnaCBlYWNoIGxpbmUgYmVsb3cgc3RlcCBieSBzdGVwLiBUaGUgZmlyc3QgbGluZSBjcmVhdGVzIGEgbmV3IGJsYW5rIGdncGxvdCBwbG90LiBTb21lIHBlb3BsZSBzZXQgYGFlcygpYCBhbmQgYGRhdGFgIGluIGBnZ3Bsb3QoKWAsIGJ1dCBzaW5jZSBkaWZmZXJlbnQgbGF5ZXJzIGNhbiB0YWtlIGRpZmZlcmVudCBgYWVzKClgIGFuZCBgZGF0YWAsIEkgbGlrZSB0byBzZXQgdGhvc2UgaW4gdGhlIGdlb20gZnVuY3Rpb25zLiANCg0KVW5saWtlIG90aGVyIGB0aWR5dmVyc2VgIGZ1bmN0aW9ucywgeW91IGNhbiB0aWUgdG9nZXRoZXIgYGdncGxvdDJgIGZ1bmN0aW9ucyB3aXRoIGArYCBpbnN0ZWFkIG9mIGAlPiVgLCBidXQgb3RoZXJ3aXNlIGl0IHdvcmtzIHRoZSBzYW1lLiBTbyB0byBvdXIgYmxhbmsgcGxvdCwgd2UncmUgYWRkaW5nIGBnZW9tX3BvaW50KClgIHdoaWNoIGlzIHRoZSBmdW5jdGlvbiBmb3IgYSBzY2F0dGVycGxvdC4gV2UncmUgdGVsbGluZyBgZ2VvbV9wb2ludCgpYCB0aGF0IGFsbCB0aGUgZGF0YSBpdCBuZWVkcyBzaG91bGQgYmUgaW4gYGxhcnZhbEFidW5kYW5jZWAgYW5kIHdoYXQgYWVzdGhldGljIG1hcHBpbmcgd2Ugd2FudCAoYGFlcygpYCkgd2hpY2ggc2V0cyB0aGUgdmFyaWFibGVzIHVzZWQgaW4gdGhlIHBsb3QgYW5kIHRha2VzIGFyZ3VtZW50cyBsaWtlIGB4YCwgYHlgLCBgY29sb3VyYCwgYHNpemVgLiANCg0KRmluYWxseSwgSSBzZXQgdGhlIHRoZW1lIGFzIGB0aGVtZV9jbGFzc2ljYCBiZWNhdXNlIHRoZSBkZWZhdWx0IHBsb3RzIGFyZSB1Z2x5ISAodHJ5IGl0LCBJIGRhcmUgeW91KQ0KIyANCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9wb2ludChkYXRhID0gbGFydmFsQWJ1bmRhbmNlLCBhZXMoeD1NYXJnYXJpdGVzLnNwcC4seT1EaWFwaGFuYS5taW51dGEpKSArDQogICAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KWWVhaCwgdGhhdCB3YXMgd2F5IG1vcmUgY29kZSB0aGFuIGJhc2UgcGxvdCBqdXN0IHRvIGdldCBhIHNpbXBsZSBwbG90ISBCdXQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgYmFzaWMgZ2dwbG90IHNrZWxldG9uLCBvdXIgaW52ZXN0bWVudCBpcyBnb2luZyB0byBwYXkgb2ZmIGJlY2F1c2UgaXQncyBub3cgc3VwZXIgZWFzeSB0byBtb2RpZnkuIExldCdzIGFzc2lnbiBkaWZmZXJlbnQgY29sb3VycyBmb3IgZGlmZmVyZW50IGRlcHRocy4NCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9wb2ludChkYXRhID0gbGFydmFsQWJ1bmRhbmNlLCBhZXMoeD1NYXJnYXJpdGVzLnNwcC4seT1EaWFwaGFuYS5taW51dGEsY29sb3VyPWFzLmNoYXJhY3RlcihkZXB0aCkpKSArDQogICAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KSSBoYWQgdG8gY29udmVydCBgeWVhcmAgYW5kIGBkZXB0aGAgdG8gY2hhcmFjdGVycyBzaW5jZSBnZ3Bsb3QgYXNzdW1lcyB0aGF0IG51bWVyaWMgdmFyaWFibGVzIGFyZSBjb250aW5vdXMuIExldCdzIGNvbWJpbmUgd2hhdCB3ZSBsZWFybmVkIGFib3ZlIGFib3V0IHRoZSB0aWR5dmVyc2UuDQoNCmBgYHtyfQ0KIyBwcmVwYXJlIGRhdGEgZm9yIHBsb3R0aW5nDQpsYXJ2YWxBYnVuZGFuY2VQbG90IDwtIGxhcnZhbEFidW5kYW5jZSAlPiUgDQogICAgbXV0YXRlKFllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksDQogICAgICAgICAgIERlcHRoID0gYXMuY2hhcmFjdGVyKGRlcHRoKSkNCg0KIyBwbG90IG5ldyBkYXRhDQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9wb2ludChkYXRhPWxhcnZhbEFidW5kYW5jZVBsb3QsYWVzKHg9TWFyZ2FyaXRlcy5zcHAuLHk9RGlhcGhhbmEubWludXRhLGNvbG91cj1EZXB0aCxzaGFwZT1ZZWFyKSkgKw0KICAgIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCkxldCdzIGFkZCBhIGxpbmVhciByZWdyZXNzaW9uIGFuZCBtYWtlIHRoZSBheGVzIGxvZyBzY2FsZToNCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9wb2ludChkYXRhPWxhcnZhbEFidW5kYW5jZVBsb3QsYWVzKHg9TWFyZ2FyaXRlcy5zcHAuLHk9RGlhcGhhbmEubWludXRhLGNvbG91cj1EZXB0aCxzaGFwZT1ZZWFyKSkgKw0KICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSsNCiAgICBzY2FsZV94X2xvZzEwKCkrDQogICAgc2NhbGVfeV9sb2cxMCgpKw0KICAgIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNClRyeSBjb21tZW50aW5nIG91dCAoYCNgKSBzb21lIG9mIHRob3NlIGxpbmVzIHRvIHNlZSB3aGF0IHRoZXkgZG8uDQoNCk5vdyB0aGF0IHlvdSB1bmRlcnN0YW5kIGhvdyB0aGF0IHdvcmtzLCBsZXQncyB0cnkgYSBmZXcgZGlmZmVyZW50IHBsb3QgdHlwZXMNCg0KYGBge3J9DQojIHByZXBhcmUgbG9uZyBkYXRhIGZvciBwbG90dGluZw0KbGFydmFsQWJ1bmRhbmNlTG9uZ1Bsb3QgPC0gbGFydmFsQWJ1bmRhbmNlTG9uZyAlPiUgDQogICAgbXV0YXRlKFllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksDQogICAgICAgICAgIERlcHRoID0gYXMuY2hhcmFjdGVyKGRlcHRoKSkNCg0KIyBwbG90IG5ldyBkYXRhDQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9ib3hwbG90KGRhdGE9bGFydmFsQWJ1bmRhbmNlTG9uZ1Bsb3QsYWVzKHg9RGVwdGgseT1sYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyxmaWxsPURlcHRoKSkgKw0KICAgIHNjYWxlX3lfbG9nMTAoKSsNCiAgICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpSZWFkeSB0byBnZXQgeW91ciBtaW5kIGJsb3duPyBCeSBhZGRpbmcgMSBsaW5lIHRvIHRoZSBhYm92ZSwgd2UgY2FuIGhhdmUgYSBwbG90IGZvciBlYWNoIHNwZWNpZXMNCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9ib3hwbG90KGRhdGE9bGFydmFsQWJ1bmRhbmNlTG9uZ1Bsb3QsYWVzKHg9RGVwdGgseT1sYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyxmaWxsPURlcHRoKSkgKw0KICAgIHNjYWxlX3lfbG9nMTAoKSsNCiAgICBmYWNldF93cmFwKH5zY2llbnRpZmljTmFtZSkrDQogICAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KT3Igc3dhcHBpbmcgb3V0IHRoZSBnZW9tIGdpdmVzIHlvdSBhIGNvbXBsZXRlbHkgZGlmZmVyZW50IChiZXR0ZXIpIHBsb3QuDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICAgIGdlb21fdmlvbGluKGRhdGE9bGFydmFsQWJ1bmRhbmNlTG9uZ1Bsb3QsYWVzKHg9RGVwdGgseT1sYXJ2YWxBYnVuZGFuY2VJbmRQZXJtMyxmaWxsPURlcHRoKSkgKw0KICAgIHNjYWxlX3lfbG9nMTAoKSsNCiAgICBmYWNldF93cmFwKH5zY2llbnRpZmljTmFtZSkrDQogICAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KT3Igc3dhcHBpbmcgb3V0IHRoZSBnZW9tIGFnYWluIGFuZCB3ZSBjYW4gbWFwIHRoZSBhYnVuZGFuY2UuDQoNCmBgYHtyfQ0KIyBwcmVwYXJlIGxvbmcgZGF0YSBmb3IgcGxvdHRpbmcNCmxhcnZhbEFidW5kYW5jZUxvbmdQbG90IDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lIA0KICAgIGdyb3VwX2J5KHNjaWVudGlmaWNOYW1lLGRlY2ltYWxMb25naXR1ZGUsZGVjaW1hbExhdGl0dWRlKSAlPiUgDQogICAgc3VtbWFyaXplKG1lYW5fSW5kUGVybTMgPSBtZWFuKGxhcnZhbEFidW5kYW5jZUluZFBlcm0zKSkNCg0KIyBwbG90IG5ldyBtZWFuIGRhdGENCmdncGxvdCgpICsNCiAgICBnZW9tX3BvaW50KGRhdGE9bGFydmFsQWJ1bmRhbmNlTG9uZ1Bsb3QsYWVzKHg9ZGVjaW1hbExvbmdpdHVkZSx5PWRlY2ltYWxMYXRpdHVkZSxzaXplPWxvZzEwKG1lYW5fSW5kUGVybTMpKSkgKw0KICAgIGZhY2V0X3dyYXAofnNjaWVudGlmaWNOYW1lKSsNCiAgICB0aGVtZV9jbGFzc2ljKCkNCg0KYGBgDQoNCkxldCdzIGFkZCBhIGJpdCBtb3JlIHRvIHRoZXNlIG1hcHMgd2l0aCBzb21lIHByb3BlciBtYXBwaW5nIHRvb2xzLiBCdXQgYmVmb3JlIHdlIG1vdmUgYXdheSBmcm9tIGdncGxvdCwgYm9va21hcmsgdGhpcyBleGNlbGxlbnQgIltSIEdyYXBoIENhdGFsb2ddKGh0dHA6Ly9zaGlueS5zdGF0LnViYy5jYS9yLWdyYXBoLWNhdGFsb2cvKSIuIFlvdSBjYW4gdmlzdWFsbHkgZmluZCBhIGdyYXBoIHlvdSB3YW50IHRvIG1ha2UgYW5kIGdldCB0aGUgY29kZSEgU3VwZXIgdXNlZnVsIQ0KDQojIE1hcHBpbmcNCg0KWW91IGNhbiBhbHdheXMgcGxvdCBhbnkgc3BhdGlhbCBkYXRhIGFuZCB1c2UgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBhcyBub3JtYWwgQ2FydGVzaWFuIGNvb3JkaW5hdGVzIGFzIHdlIGRpZCBhYm92ZSwgYnV0IGlmIHlvdSB3YW50IHRvIGRvIGFueXRoaW5nIHdpdGggcHJvamVjdGlvbnMsIG9yIG90aGVyIEdJUyB0eXBlIG9wZXJhdGlvbnMgKGJ1ZmZlciwgaW50ZXJzZWN0aW9uLCBvdmVybGFwcywgZXRjKSwgeW91IHJlYWxseSBuZWVkIHRvIGRpZyBhIGxpdHRsZSBkZWVwZXIuIA0KDQpQcm9qZWN0aW9uIGFuZCBkYXR1bSBpbmZvcm1hdGlvbiBpbiBSIGluIGJvdGggYHNwYCBhbmQgYHNmYCAoYXMgd2VsbCBhcyBtYW55IG90aGVyIGFwcGxpY2F0aW9ucykgYXJlIGhhbmRsZWQgYnkgdGhlIFtwcm9qLjRdKGh0dHA6Ly9wcm9qNC5vcmcvaW5kZXguaHRtbCkgc3RhbmRhcmQuIFdlIHdvbid0IGdvIGluIHRvIGRlZXAgaW50byB0aGUgJ2hvdyB0byBjaG9vc2UgYSBwcm9qZWN0aW9uJywgYnV0IHRoZXJlIGlzIHN1YnN0YW50aWFsIGluZm9ybWF0aW9uIHJlZ2FyZGluZyB0aGUgcGFyYW1ldGVycyBvbiB0aGUgW3Byb2ouNF0oaHR0cDovL3Byb2o0Lm9yZy9pbmRleC5odG1sKSB3ZWJzaXRlIGFuZCB0aGlzIFtQcm9qZWN0aW9uIFdpemFyZF0oaHR0cDovL3Byb2plY3Rpb253aXphcmQub3JnLykgaXMgc3VwZXIgaGVscGZ1bCBmb3IgcmVjb21tZW5kaW5nIHByb2plY3Rpb24gKGFuZCBwcm9kdWNpbmcgZm9ybWF0dGVkIHByb2o0IHN0cmluZ3MpIGJhc2VkIG9uIHRoZSBleHRlbnQgb2YgeW91ciBtYXAgYW5kIHdoYXQgeW91IHdhbnQgdG8gb3B0aW1pemUgZm9yIChpLmUuIEVxdWFsLWFyZWEsIENvbmZvcm1hbCwgRXF1aWRpc3RhbnQsIENvbXByb21pc2UpLg0KDQpCZSBhd2FyZSB0aGF0IGluIG1vc3QgY2FzZXMgUiB3aWxsIHRocm93IGFuIGVycm9yIGlmIHlvdSBhcmUgdHJ5aW5nIHRvIHVzZSBtdWx0aXBsZSBzcGF0aWFsIG9iamVjdHMgd2l0aCBkaWZmZXJlbnQgdHJhbnNmb3JtYXRpb25zLCBzbyByZW1lbWJlciB0byB0cmFuc2Zvcm0gdGhlbSB0byBhIGNvbW1vbiBwcm9qZWN0aW9uIGJlZm9yZSB0cnlpbmcgdG8gY29tYmluZSB0aGVtLiBBbHNvLCByZW1lbWJlciB0aGF0IHNvbWUgcHJvamVjdGlvbnMgaGF2ZSB1bml0cyBvZiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGFuZCBzb21lIGhhdmUgdW5pdHMgb2YgbWV0ZXJzIGFuZCB0aGV5IGFyZSBvZiBjb3Vyc2UgaW5jb21wYXRpYmxlLg0KDQojIyBUaGUgY2xhc3NpYyBhbmQgdGhlIGN1dHRpbmcgZWRnZTogYHNwYCB2cyBgc2ZgDQoNCkJlZm9yZSBOb3ZlbWJlciAyMDE2LCBpZiB5b3Ugd2FudGVkIHRvIGRvIGFueSBzcGF0aWFsIHRhc2tzIGluIFIgeW91ciBjaG9pY2Ugd2FzIGEgc2ltcGxlIG9uZS4gWW91IG5lZWRlZCB0byB1c2UgdGhlIGBzcGAgcGFja2FnZSAoYWxvbmcgd2l0aCBgcmdlb3NgLCBhbmQgYHJnZGFsYCkuIE5vdywgdGhlIGBzZmAgcGFja2FnZSBpcyByZXBsYWNpbmcgdGhhdCBzZXQgb2YgcGFja2FnZXMsIGFuZCB0aGF0J3MgYSBnb29kIHRoaW5nIGJlY2F1c2UgaXQgYWxsb3dzIGZvciBmYXN0ZXIgY29tcHV0YXRpb24gYW5kICoqaXQgaXMgY29tcGF0aWJsZSB3aXRoIHRoZSBgdGlkeXZlcnNlYCoqLiBIb3dldmVyLCBtYW55IG9mIHRoZSBoZWxwZXIgcGFja2FnZXMgdGhhdCB3ZXJlIGJ1aWx0IHRvIGludGVyYWN0IHdpdGggYHNwYCBoYXZlIG5vdCBiZWVuIHVwZ3JhZGVkIHRvIGBzZmAgKHlldCkuDQoNCiMjIyBUaGUgYHNwYCBwYWNrYWdlDQoNClRoZSBicmVhZCBhbmQgYnV0dGVyICdTcGF0aWFsJyBvYmplY3QgY2xhc3MgZnJvbSB0aGUgYHNwYHBhY2thZ2UgYXJlIG9mIHRoZSBmb3JtIGBTcGF0aWFsQkxBTktEYXRhRnJhbWVgIHdoZXJlIGJsYW5rIGNhbiBiZSBgUG9pbnRzYCwgYExpbmVzYCwgYFBvbHlnb25zYCwgZXRjLiBMZXQncyBjcmVhdGUgYSBgU3BhdGlhbFBvaW50c0RhdGFGcmFtZWAgZnJvbSBvdXIgYGxhcnZhbEFidW5kYW5jZUxvbmdgDQoNCmBgYHtyfQ0KIyBmaXJzdCBtYWtlIHNwYXRpYWwgcG9pbnRzIGFuZCBzZXQgdGhlIHByb2plY3Rpb24gKG1vcmUgb24gdGhhdCBiZWxvdykNCnNwb2ludHMgPC0gU3BhdGlhbFBvaW50cyhjb29yZHMgPSBsYXJ2YWxBYnVuZGFuY2VMb25nWyxjKCJkZWNpbWFsTG9uZ2l0dWRlIiwiZGVjaW1hbExhdGl0dWRlIildLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nID0gQ1JTKCIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCIpKQ0KDQojIHRoZW4gbWFrZSBhIFNwYXRpYWxQb2ludHNEYXRhRnJhbWUgYnkgYWRkaW50IHRoZSBkYXRhIGZyYW1lIGJhY2sgaW4NCnNwb2ludHNkZiA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKHNwb2ludHMsbGFydmFsQWJ1bmRhbmNlTG9uZykNCg0KIyBub3cgZm9yIGVhY2ggcG9pbnQgaW4gc3BvaW50c2RmIHRoZXJlIGlzIGEgcm93IGluIHRoZSBkYXRhIGZyYW1lIHdoaWNoIGNhbiBiZSBhY2Nlc3NlZCBieSBzcG9pbnRzZGZAZGF0YSwgb3IgaWYgeW91IHdhbnQgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIHNwb2ludHNkZiR5ZWFyIChvciBzcG9pbnRzZGZAZGF0YSR5ZWFyKSwgYnV0IHRoZSBkYXRhIGFuZCB0aGUgZ2VvbWV0cmllcyBhcmUgYSBsaXR0bGUgZGlzam9pbnQuDQoNCiMgcGxvdCBpdA0KcGxvdChzcG9pbnRzZGYpDQpgYGANCg0KVGhhdCdzIG5vdCBwYXJ0aWN1bGFybHkgdXNlZnVsIHdpdGhvdXQgYSBiYXNlbWFwLCBzbyBsZXQncyB1c2UgdGhlIGByYXN0ZXJgIHBhY2thZ2UgdG8gZ2V0IHNvbWUgaGlnaCByZXNvbHV0aW9uIHByb3ZpbmNlIGxldmVsIGJvdW5kYXJpZXMgZGF0YSAobm90IHRoZSBvbmx5IHdheSB0byBnZXQgY29hc3RsaW5lcywgYnV0IG9uZSBvZiBteSBmYXZvcml0ZSkuDQoNCmBgYHtyfQ0KQ2FuYWRhIDwtIGdldERhdGEoJ0dBRE0nLCBjb3VudHJ5PSJDQU4iLCBsZXZlbD0xKQ0KcGxvdChDYW5hZGEpDQoNCiMgdGhpcyBpcyBhIGxpdHRsZSBleGNlc3NpdmUgc2luY2Ugd2Ugb25seSBuZWVkIE5vdmEgU2NvdGlhDQoNCk5TIDwtIENhbmFkYVtDYW5hZGEkTkFNRV8xPT0iTm92YSBTY290aWEiLF0NCnBsb3QoTlMsYWRkPVQsY29sPSdibHVlJykNCmBgYA0KDQpOb3cgbGV0J3MgY29tYmluZSBteSBkYXRhIHdpdGggdGhlIGNvYXN0bGluZQ0KYGBge3J9DQojIHNlZSB0aGUgYm91bmRpbmcgYm94IG9mIHRoZSBsYXJ2YWwgZGF0YQ0KYmJveChzcG9pbnRzZGYpDQoNCiMgdHJhbnNmb3JtIGJvdGggU3BhdGlhbCBvYmplY3RzDQpwcm9qX2FlYSA8LSAiK3Byb2o9YWVhICtsYXRfMT00NS4zNDIyOTM3MTk1Njk0NCArbGF0XzI9NDYuMTU0NDI2ODg0NzY1NDg1ICtsb25fMD0tNjEuNzA0NzExOTE0MDYyNTEiDQpzcG9pbnRzZGZfYWVhIDwtIHNwVHJhbnNmb3JtKHNwb2ludHNkZixwcm9qX2FlYSkNCk5TX2FlYSA8LSBzcFRyYW5zZm9ybShOUyxwcm9qX2FlYSkNCg0KIyBzZWUgdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgbGFydmFsIGRhdGEgYWZ0ZXIgcHJvamVjdGlvbg0KYmJveChzcG9pbnRzZGZfYWVhKQ0KDQpwbG90KHNwb2ludHNkZl9hZWEscGNoPTE2LCB4bGltID0gYygtMTUwMDAsMTIwMDApLCB5bGltID0gYyg0NzA1MDAwLDQ3NTUwMDApKQ0KcGxvdChOU19hZWEsIGNvbCA9ICdncmV5JyxhZGQ9VCkNCmBgYA0KDQpZb3UgY2FuIGZpbmQgbW9yZSBpbmZvIGFib3V0IHRoZSBvdGhlciBTcGF0aWFsIGNsYXNzZXMgaW4gbXkgW0dJUyB0dXRvcmlhbF0oaHR0cDovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby9HSVNfbWFwcGluZ19pbl9SLykgSSB3cm90ZSBiZWZvcmUgYHNmYCB3YXMgY3JlYXRlZC4NCg0KIyMjIFRoZSBgc2ZgIHBhY2thZ2UNCg0KVGhlIGBzZmAgcGFja2FnZSBpcyBhbiBhbiBSIGltcGxlbWVudGF0aW9uIG9mICJTaW1wbGUgRmVhdHVyZXMiIChvZmZpY2lhbGx5IFNpbXBsZSBGZWF0dXJlIEFjY2Vzcykgd2hpY2ggaXMgYm90aCBhbiBPcGVuIEdlb3NwYXRpYWwgQ29uc29ydGl1bSAoT0dDKSBhbmQgSW50ZXJuYXRpb25hbCBPcmdhbml6YXRpb24gZm9yIFN0YW5kYXJkaXphdGlvbiAoSVNPKSBzdGFuZGFyZCBJU08gMTkxMjUgdGhhdCBzcGVjaWZpZXMgYSBjb21tb24gc3RvcmFnZSBhbmQgYWNjZXNzIG1vZGVsIG9mIG1vc3RseSB0d28tZGltZW5zaW9uYWwgZ2VvbWV0cmllcyAocG9pbnQsIGxpbmUsIHBvbHlnb24sIG11bHRpLXBvaW50LCBtdWx0aS1saW5lLCBldGMuKSB1c2VkIGJ5IGdlb2dyYXBoaWMgaW5mb3JtYXRpb24gc3lzdGVtcy4NCg0KVGhlIGJyZWFkIGFuZCBidXR0ZXIgb2JqZWN0IGNsYXNzZXMgaW4gdGhlIGBzZmAgcGFja2FnZSBhcmUgYHNmYCAoY3JlYXRlZCBieSBgc3Rfc2YoKWApIHdoaWNoIGFyZSBlZmZlY3RpdmVseSBhIG5vcm1hbCBkYXRhIGZyYW1lIHdpdGggdGhlIGFkZGl0aW9uIG9mIG9uZSAob3IgbW9yZSkgc2ltcGxlIGZlYXR1cmVzIGxpc3QgY29sdW1uIChgc2ZjYDsgY3JlYXRlZCBieSBgc3Rfc2ZjKClgKS4gVGhpcyBgc2ZjYCBjb2x1bW4sIHdoaWNoIGlzIHJlYWxseSBqdXN0IGFub3RoZXIgY29sdW1uIGluIHlvdXIgZGF0YSBmcmFtZSwgY29udGFpbnMgdGhlIGdlb21ldHJpYyBpbmZvcm1hdGlvbiB0aGF0IGdldHMgcGxvdHRlZCBvbiBhIG1hcC4gVGhpcyBnZW9tZXRyaWMgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGFzIGEgc2ltcGxlIGZlYXR1cmUgd2hpY2ggY2FuIHRha2UgdGhlIGZvcm0gb2YgcG9seWdvbihzKSAoY3JlYXRlZCBieSBgc3RfcG9seWdvbigpYCBvciBgc3RfbXVsdGlwb2x5Z29uKClgICksIGxpbmUocykgKGNyZWF0ZWQgYnkgYHN0X3N0X2xpbmVzdHJpbmcoKWAgb3IgYHN0X211bHRpc3RfbGluZXN0cmluZygpYCApLCBvciBwb2ludChzKSAoY3JlYXRlZCBieSBgc3RfcG9pbnQoKWAgb3IgYHN0X211bHRpcG9pbnQoKWAgKQ0KDQpMZXQncyBzdGFydCBieSB0dXJuaW5nIG91ciBgbGFydmFsQWJ1bmRhbmNlTG9uZ2AgaW50byBhbiBgc2ZgIG9iamVjdCBhbmQgdHJhbnNmb3JtIGl0IGludG8gb3VyIGRlc2lyZWQgcHJvamVjdGlvbiBpbiAxIGNvbW1hbmQgKHJlbWluZGVyOiBgc2ZgIGlzIGNvbXBhdGlibGUgd2l0aCB0aGUgdGlkeXZlcnNlKToNCg0KYGBge3J9DQpsYXJ2YWxBYnVuZGFuY2VMb25nX3NmIDwtIGxhcnZhbEFidW5kYW5jZUxvbmcgJT4lIA0KICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoImRlY2ltYWxMb25naXR1ZGUiLCAiZGVjaW1hbExhdGl0dWRlIiksDQogICAgICAgICAgICAgY3JzPSIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCIsDQogICAgICAgICAgICAgcmVtb3ZlPUZBTFNFKSAlPiUgDQogICAgc3RfdHJhbnNmb3JtKHByb2pfYWVhKQ0KDQpnZ3Bsb3QoKSsNCiAgICBnZW9tX3NmKGRhdGEgPSBsYXJ2YWxBYnVuZGFuY2VMb25nX3NmKQ0KYGBgDQpBcyB5b3UgbWF5IGhhdmUgbm90aWNlZCwgYGdlb21fc2ZgIGRvZXMgbm90ICpuZWVkKiBhIGRlZmluZWQgYGFlcygpYCBzaW5jZSBpcyBhbHJlYWR5IGtub3dzIHRoYXQgbGF0aXR1ZGUgaXMgeW91ciB5LWF4aXMgYW5kIGxvbmdpdHVkZSBpcyB0aGUgeC1heGlzLiBZb3UgY2FuIG9mIGNvdXJzZSB0b2dnbGUgYGdyb3VwYCwgYGNvbG91cmAsIGBzaXplYCwgYW5kIGBmaWxsYCBpbiB0aGUgYGFlc2AgYmFzZWQgb24gb3RoZXIgY29sdW1ucyBpbiB5b3VyIGRhdGEgZnJhbWUuDQoNCkxldCdzIHBsb3QgaXMgd2l0aCBhIGJhc2VtYXAhIGByYXN0ZXJgIG9ubHkgcHJvdmlkZXMgdGhlIEdBRE0gY29hc3RsaW5lIGRhdGEgaW4gYHNwYCBmb3JtYXQsIGJ1dCB3ZSBjYW4gY29udmVydCB3aXRoIGBzdF9hc19zZigpYCBhbmQgdGhlbiBgZmlsdGVyKClgIHRvIGdldCBqdXN0IHRoZSBNYXJpdGltZSBwcm92aW5jZXMgd2l0aCBgZHBseXJgDQpgYGB7cn0NCk1hclByb3Zfc2YgPC0gZ2V0RGF0YSgnR0FETScsIGNvdW50cnk9IkNBTiIsIGxldmVsPTEpICU+JSANCiAgICBzdF9hc19zZigpICU+JSANCiAgICBmaWx0ZXIoTkFNRV8xPT0iTm92YSBTY290aWEifA0KICAgICAgICAgICAgICAgTkFNRV8xPT0iUHJpbmNlIEVkd2FyZCBJc2xhbmQifA0KICAgICAgICAgICAgICAgTkFNRV8xPT0iTmV3IEJydW5zd2ljayIpJT4lIA0KICAgIHN0X3RyYW5zZm9ybShwcm9qX2FlYSkNCg0KZ2dwbG90KCkrDQogICAgZ2VvbV9zZihkYXRhID0gbGFydmFsQWJ1bmRhbmNlTG9uZ19zZikrDQogICAgZ2VvbV9zZihkYXRhID0gTWFyUHJvdl9zZikNCg0KYGBgDQoNCkFuZCB0byBtYWtlIHRoaW5ncyBmdW4sIGxldCdzIGhhdmUgb25lIHpvb21lZCBpbiBwbG90IHdpdGggdGhlIGRvdHMsIGFuZCBhIHpvb21lZCBvdXQgcGxvdCB3aXRoIGEgJ3pvb20gYm94Jw0KYGBge3IsIGJvdW5kaW5nIGJveH0NCiNkZWZpbmUgdGhlIGNvcm5lcnMgKHdpdGggYSBidWZmZXIpDQpsYXJ2YWxiYm94IDwtIHN0X2J1ZmZlcihsYXJ2YWxBYnVuZGFuY2VMb25nX3NmLCBkaXN0ID0gMTAwMDApICU+JSANCiAgICBzdF9iYm94KCkNCg0KI2NyZWF0ZSBhIG1hdHJpeCB0aGF0IHRyYWNlcyBhIGxpbmUgdGhlIHdob2xlIHdheSBhcm91bmQgeW91ciBib3gNCmJic2ZjIDwtIGNiaW5kKA0KICAgICAgICAgICAgYyhsYXJ2YWxiYm94WzFdLGxhcnZhbGJib3hbM10sbGFydmFsYmJveFszXSxsYXJ2YWxiYm94WzFdLGxhcnZhbGJib3hbMV0pLA0KICAgICAgICAgICAgYyhsYXJ2YWxiYm94WzJdLGxhcnZhbGJib3hbMl0sbGFydmFsYmJveFs0XSxsYXJ2YWxiYm94WzRdLGxhcnZhbGJib3hbMl0pDQogICAgICAgICkgJT4lIA0KICAgIGxpc3QoKSAlPiUgICAgICAgICAgICAgICAgICAjIHB1dCB0aGF0IG1hdHJpeCBpbiBhIGxpc3QNCiAgICBzdF9wb2x5Z29uKCkgJT4lICAgICAgICAgICAgIyBwdXQgdGhhdCBsaXN0IGluIGEgc2ltcGxlIGZlYXR1cmVzIHBvbHlnb24NCiAgICBzdF9zZmMoY3JzID0gcHJval9hZWEpICU+JSAgIyBwdXQgdGhhdCBwb2x5Z29uIGludG8gYSBzaW1wbGUgZmVhdHVyZXMgY29sdW1uIGFuZCBnaXZlIHByb2plY3Rpb24gaW5mbw0KICAgIHN0X3NmKG5hbWU9IlN0dWR5IFNpdGUiLCBnZW9tZXRyeT0uKSAgICAjIGZpbmFsbHkgbWFrZSBpdCBhbiBzZiBvYmplY3QNCg0KIyBwbG90IGl0IQ0KcDEgPC0gZ2dwbG90KCkrDQogICAgZ2VvbV9zZihkYXRhID0gTWFyUHJvdl9zZikrDQogICAgZ2VvbV9zZihkYXRhID0gYmJzZmMsIHNpemUgPSAyLCBmaWxsID0gInRyYW5zcGFyZW50IikNCg0KcDIgPC0gZ2dwbG90KCkrDQogICAgZ2VvbV9zZihkYXRhID0gbGFydmFsQWJ1bmRhbmNlTG9uZ19zZikrDQogICAgZ2VvbV9zZihkYXRhID0gTWFyUHJvdl9zZikgKw0KICAgIGNvb3JkX3NmKHhsaW0gPSBsYXJ2YWxiYm94W2MoMSwzKV0seWxpbSA9IGxhcnZhbGJib3hbYygyLDQpXSkNCg0KZ3JpZC5hcnJhbmdlKHAxLHAyKQ0KYGBgDQoNCg0KRmluYWxseSwgbGV0J3MgYnJpbmcgZmFjZXRzIGJhY2shDQoNCmBgYHtyfQ0KIyBwcmVwYXJlIGxvbmcgZGF0YSBmb3IgcGxvdHRpbmcNCmxhcnZhbEFidW5kYW5jZUxvbmdQbG90IDwtIGxhcnZhbEFidW5kYW5jZUxvbmdfc2YgJT4lIA0KICAgIGdyb3VwX2J5KHNjaWVudGlmaWNOYW1lLGRlY2ltYWxMb25naXR1ZGUsZGVjaW1hbExhdGl0dWRlKSAlPiUgDQogICAgc3VtbWFyaXplKG1lYW5fSW5kUGVybTMgPSBtZWFuKGxhcnZhbEFidW5kYW5jZUluZFBlcm0zKSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgbXV0YXRlKGxhdF9hZWEgPSB1bmxpc3QobGFwcGx5KGdlb21ldHJ5LCdbJywyKSksDQogICAgICAgICAgIGxvbmdfYWVhID0gdW5saXN0KGxhcHBseShnZW9tZXRyeSwnWycsMSkpKQ0KDQoNCnAzIDwtIGdncGxvdCgpKw0KICAgIGdlb21fcG9pbnQoZGF0YSA9IGxhcnZhbEFidW5kYW5jZUxvbmdQbG90LCBhZXMoeSA9IGxhdF9hZWEsIHggPSBsb25nX2FlYSwgc2l6ZT1tZWFuX0luZFBlcm0zKSkrDQogICAgZ2VvbV9zZihkYXRhID0gTWFyUHJvdl9zZikgKw0KICAgIGZhY2V0X3dyYXAofnNjaWVudGlmaWNOYW1lKSsNCiAgICBjb29yZF9zZih4bGltID0gbGFydmFsYmJveFtjKDEsMyldLHlsaW0gPSBsYXJ2YWxiYm94W2MoMiw0KV0pIA0KcDMNCmBgYA0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCiMgb2gsIGFuZCBpZiB5b3Ugd2FudCB0byBzYXZlIGEgcGxvdCwgcGljayB5b3VyIGZvcm1hdCwgc2l6ZSwgYW5kIHJlc29sdXRpb24NCmdnc2F2ZShmaWxlbmFtZT0ic3BlY2llc2FidW5kYW5jZS50aWZmIiwgcGxvdCA9IHAzLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxNi41LCB1bml0ID0gImNtIiwgZHBpID0gMzAwKQ0KZ2dzYXZlKGZpbGVuYW1lPSJzcGVjaWVzYWJ1bmRhbmNlLnBkZiIsIHBsb3QgPSBwMywgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTYuNSwgdW5pdCA9ICJjbSIsIGRwaSA9IDMwMCkNCmdnc2F2ZShmaWxlbmFtZT0ic3BlY2llc2FidW5kYW5jZS5wbmciLCBwbG90ID0gcDMsIHdpZHRoID0gMjAsIGhlaWdodCA9IDE2LjUsIHVuaXQgPSAiY20iLCBkcGkgPSAzMDApDQoNCg0KYGBgDQoNCg0KIyBJbXBvcnRhbnQgbGlua3MNCg0KW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cDovL3I0ZHMuaGFkLmNvLm56LykNCltSIEdyYXBoIENhdGFsb2ddKGh0dHA6Ly9zaGlueS5zdGF0LnViYy5jYS9yLWdyYXBoLWNhdGFsb2cvKQ0KQSBbR0lTIHR1dG9yaWFsXShodHRwOi8vcmVtaS1kYWlnbGUuZ2l0aHViLmlvL0dJU19tYXBwaW5nX2luX1IvKSB3cml0dGVuIGJ5IFJlbWkgKG1vc3RseSBpbiBgc3BgKQ0KUHJvamVjdGlvbiBzdHVmZjogW3Byb2ouNF0oaHR0cDovL3Byb2o0Lm9yZy9pbmRleC5odG1sKSBhbmQgW1Byb2plY3Rpb24gV2l6YXJkXShodHRwOi8vcHJvamVjdGlvbndpemFyZC5vcmcvKQ0KDQoNCiMgQ2hhbGxlbmdlIGFuc3dlcnMNCj4gKipDaGFsbGVuZ2UgMSBzb2x1dGlvbiAqKg0KPmBgYHtyLGV2YWw9RkFMU0V9DQo+c2l0ZTUgPC0gbGFydmFsQWJ1bmRhbmNlTG9uZyAlPiUNCj4gICAgZmlsdGVyKGxvY2F0aW9uSUQ9PTUpICU+JQ0KPiAgICBzZWxlY3QoLWxvY2F0aW9uSUQsLWRlY2ltYWxMYXRpdHVkZSwtZGVjaW1hbExhdGl0dWRlKQ0KPmBgYA0KPioqTm90ZToqKiBUaGUgb3JkZXIgb2Ygb3BlcmF0aW9ucyBpcyB2ZXJ5IGltcG9ydGFudCBpbiB0aGlzIGNhc2UuIElmIHdlIHVzZWQgJ3NlbGVjdCcgZmlyc3QsIGZpbHRlciB3b3VsZCBub3QgYmUgYWJsZSB0byBmaW5kIHRoZSB2YXJpYWJsZSBjb250aW5lbnQgc2luY2Ugd2Ugd291bGQgaGF2ZSByZW1vdmVkIGl0IGluIHRoZSBwcmV2aW91cyBzdGVwLg0KDQoNCj4gKipDaGFsbGVuZ2UgMiBzb2x1dGlvbiAqKg0KPmBgYHtyfQ0KPkluZFBlcm0zX2J5TG9jYXRpb25JRCA8LSBsYXJ2YWxBYnVuZGFuY2VMb25nICU+JQ0KPiAgICBncm91cF9ieShsb2NhdGlvbklEKSAlPiUNCj4gICAgc3VtbWFyaXplKG1lYW5fSW5kUGVybTMgPSBtZWFuKGxhcnZhbEFidW5kYW5jZUluZFBlcm0zKSkgJT4lIA0KPiAgICBmaWx0ZXIobWVhbl9JbmRQZXJtMyA9PSBtaW4obWVhbl9JbmRQZXJtMykgfCBtZWFuX0luZFBlcm0zID09IG1heChtZWFuX0luZFBlcm0zKSkNCj5gYGANCg0KDQpbPDxCQUNLXShodHRwczovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby8yMDE3LUNIT05lLURhdGEvKQ0K