Sypnosis

In this report, we seek to examine which types of events are most harmful to population health and economic consequences from the data, particularly from year 1990 onwards. To investigate this, we obtained the data from the NOAA Storm Database which collects characteristics of major storms and weather events in the U.S. including types of storms, estimates of any fatalities, injuries, and property damage from the year 1950 to end November 2011.

From these data, we found out that hurricane/typhoon and tsunami events have the highest average number of casualties and economical damage per occurrence.

Loading and Processing the Raw Data

From the NOAA Storm Database, we obtained the data that monitored the major storms and weather events in the U.S. Additional documentation of the database is available which can be found at:

The accuracy of this analysis would heavily depend on the preprocessing and cleaning of the data. The cleaner and processed the data is, the more accurate the analysis and results will be. Hence, I will be putting effort into this section, so please expect this section to be lengthy.

Loading the necessary packages

The packages that are used for this analysis will be loaded.

library(ggplot2)
library(dplyr)
library(tidyr)
library(data.table)
library(lubridate)

Reading the data and exploration

After this is done, we will load the dataset using the fread function from the data.table package which is way faster than the conventional method.

storm <- fread("storm.csv")

Read 0.0% of 967216 rows
Read 32.1% of 967216 rows
Read 50.7% of 967216 rows
Read 67.2% of 967216 rows
Read 78.6% of 967216 rows
Read 88.9% of 967216 rows
Read 902297 rows and 37 (of 37) columns from 0.523 GB file in 00:00:08

Next step would be to get a quick sense of the data.

dim(storm)
[1] 902297     37
head(storm)
str(storm)
Classes ‘data.table’ and 'data.frame':  902297 obs. of  37 variables:
 $ STATE__   : num  1 1 1 1 1 1 1 1 1 1 ...
 $ BGN_DATE  : chr  "4/18/1950 0:00:00" "4/18/1950 0:00:00" "2/20/1951 0:00:00" "6/8/1951 0:00:00" ...
 $ BGN_TIME  : chr  "0130" "0145" "1600" "0900" ...
 $ TIME_ZONE : chr  "CST" "CST" "CST" "CST" ...
 $ COUNTY    : num  97 3 57 89 43 77 9 123 125 57 ...
 $ COUNTYNAME: chr  "MOBILE" "BALDWIN" "FAYETTE" "MADISON" ...
 $ STATE     : chr  "AL" "AL" "AL" "AL" ...
 $ EVTYPE    : chr  "TORNADO" "TORNADO" "TORNADO" "TORNADO" ...
 $ BGN_RANGE : num  0 0 0 0 0 0 0 0 0 0 ...
 $ BGN_AZI   : chr  "" "" "" "" ...
 $ BGN_LOCATI: chr  "" "" "" "" ...
 $ END_DATE  : chr  "" "" "" "" ...
 $ END_TIME  : chr  "" "" "" "" ...
 $ COUNTY_END: num  0 0 0 0 0 0 0 0 0 0 ...
 $ COUNTYENDN: logi  NA NA NA NA NA NA ...
 $ END_RANGE : num  0 0 0 0 0 0 0 0 0 0 ...
 $ END_AZI   : chr  "" "" "" "" ...
 $ END_LOCATI: chr  "" "" "" "" ...
 $ LENGTH    : num  14 2 0.1 0 0 1.5 1.5 0 3.3 2.3 ...
 $ WIDTH     : num  100 150 123 100 150 177 33 33 100 100 ...
 $ F         : chr  "3" "2" "2" "2" ...
 $ MAG       : num  0 0 0 0 0 0 0 0 0 0 ...
 $ FATALITIES: num  0 0 0 0 0 0 0 0 1 0 ...
 $ INJURIES  : num  15 0 2 2 2 6 1 0 14 0 ...
 $ PROPDMG   : num  25 2.5 25 2.5 2.5 2.5 2.5 2.5 25 25 ...
 $ PROPDMGEXP: chr  "K" "K" "K" "K" ...
 $ CROPDMG   : num  0 0 0 0 0 0 0 0 0 0 ...
 $ CROPDMGEXP: chr  "" "" "" "" ...
 $ WFO       : chr  "" "" "" "" ...
 $ STATEOFFIC: chr  "" "" "" "" ...
 $ ZONENAMES : chr  "" "" "" "" ...
 $ LATITUDE  : num  3040 3042 3340 3458 3412 ...
 $ LONGITUDE : num  8812 8755 8742 8626 8642 ...
 $ LATITUDE_E: num  3051 0 0 0 0 ...
 $ LONGITUDE_: num  8806 0 0 0 0 ...
 $ REMARKS   : chr  "" "" "" "" ...
 $ REFNUM    : num  1 2 3 4 5 6 7 8 9 10 ...
 - attr(*, ".internal.selfref")=<externalptr> 

Cleaning and preprocessing of data

In this analysis, I would only examine the effects from the year 2000 onwards (i.e. events on and after 01/01/2000) because the consequences of severe storm events that happened way earlier may not have the same impact as it does many decades later. This is due to infrastructures and properties are now way sturdier than before, people are also more knowledgeable and prepared than before. Hence, it is very likely that the average cost and effect of a tornado of a certain magnitude of strength is lesser now than many decades ago.

It would then, be more appropriate, to only use the data that happened in the last 16 years (i.e. from 2000 to end 2011). Let’s filter the data appropriately in the following code below.

storm$BGN_DATE <- with(storm, as.Date(mdy_hms(BGN_DATE)))
storm <- subset(storm, BGN_DATE >= '2000-01-01')
dim(storm)
[1] 523163     37

It seems like only about 380k of entries are recorded from year 1950 to 1999 (50 years). In comparison, there are 523k of entries from 2010 to 2011 (12 years)! This is helpful for us as it would also remove many old entries that probably wouldn’t contain useful information in this analysis.

Next, we would only need certain columns of this dataset for this analysis, so let’s extract those out. After doing so, let’s also have a look at the event types that can happen.

storm <- storm %>% select(EVTYPE, FATALITIES, INJURIES, PROPDMG, PROPDMGEXP, CROPDMG, CROPDMGEXP)
data.frame(table(storm$EVTYPE))

We noticed that there are many categories that could belong together, yet classfied separately due to:

  • Singular v.s. Plural form (e.g. ‘Thunderstorm’ v.s. ‘Thunderstorms’)

  • Categorized magnitudes (e.g. ‘TSTM Wind (G40)’ v.s. ‘TSTM Wind (G45)’)

  • Different namings (e.g. ‘High Surf’, ‘High Surf Advisories’, ‘High Surf Advisory’, ‘Heavy Surf’)

The next chunk of code, will proceed to clean up all these irregularities that may be present and group up as much as possible using regular expressions. Since there is no quick way to go about doing this, this process may be ‘manual’.

storm$EVTYPE[grepl('DUST', storm$EVTYPE)] <- 'DUST STORM'
storm$EVTYPE[grepl('UNUSUALLY|UNSEASONAL|UNSEASONABLY|ABNORMALLY|EXTREME|EXTREMELY|(EXCESSIVE HEAT)|(VERY WARM|DRY)|RECORD|PROLONG|(WARM WEATHER)', storm$EVTYPE)] <- 'EXTREME COLD/HOT/WET/DRY/EVENT' 
storm$EVTYPE[grepl('^DRY|^DROUGHT', storm$EVTYPE)] <- 'DROUGHT/DRY'
storm$EVTYPE[grepl('SURF|(HIGH SEAS)|(HIGH WATER)|SEICHE', storm$EVTYPE)] <- 'HIGH SURF'
storm$EVTYPE[grepl('ASTRONOMICAL|(HIGH SURF)|CURRENT|ROGUE|(ROUGH SEAS)', storm$EVTYPE)] <- 'LOW/HIGH TIDE/SURF/CURRENT'
storm$EVTYPE[grepl('TSTM WIND|(THUNDERSTORM WIND)', storm$EVTYPE)] <- 'THUNDERSTORM WIND'
storm$EVTYPE[grepl('THUNDERSTORM', storm$EVTYPE)] <- 'THUNDERSTORM WIND'
storm$EVTYPE[grepl('FLOOD|FLD|DROWNING', storm$EVTYPE)] <- 'FLOODING'
storm$EVTYPE[grepl('SNOW', storm$EVTYPE)] <- 'SNOW'
storm$EVTYPE[grepl('^COLD', storm$EVTYPE)] <- 'COLD'
storm$EVTYPE[grepl('FREEZ|FROST', storm$EVTYPE)] <- 'FREEZE/FROST'
storm$EVTYPE[grepl('FOG|SMOKE', storm$EVTYPE)] <- 'FOG/SMOKE'
storm$EVTYPE[grepl('^WINTER|WINTRY', storm$EVTYPE)] <- 'WINTER WEATHER'
storm$EVTYPE[grepl('^WIND|(STRONG WIND)|WND|GUSTY|GRADIENT', storm$EVTYPE)] <- 'WIND'
storm$EVTYPE[grepl('HAIL|(ICE STORM)|SLEET|BLIZZARD|GLAZE', storm$EVTYPE)] <- 'BLIZZARD/HAIL'
storm$EVTYPE[grepl('WILD|(BRUSH FIRE)', storm$EVTYPE)] <- 'WILD/FOREST FIRE'
storm$EVTYPE[grepl('TORNADO|WHIRLDWIND', storm$EVTYPE)] <- 'TORNADO'
storm$EVTYPE[grepl('(BLACK ICE)|(ICE ON ROAD)|(ICY ROAD)|(PATCHY ICE)', storm$EVTYPE)] <- 'ICY ROAD'
storm$EVTYPE[grepl('FUNNEL|WALL', storm$EVTYPE)] <- 'FUNNEL CLOUDS'
storm$EVTYPE[grepl('VOLCAN', storm$EVTYPE)] <- 'VOLCANIC ASH'
storm$EVTYPE[grepl('MUD', storm$EVTYPE)] <- 'MUDSLIDE'
storm$EVTYPE[grepl('HURRICANE', storm$EVTYPE)] <- 'HURRICANE/TYPHOON'
storm$EVTYPE[grepl('LAND|AVALANCHE', storm$EVTYPE)] <- 'LANDSLIDE/AVALANCHE'
storm$EVTYPE[grepl('STORM|SURGE|DEPRESSION|RAIN|PRECIPITATION', storm$EVTYPE)] <- 'HEAVY RAIN/STORMS'

Whew! After all that hardwork, now all that’s left is to do a simple quick filter to keep those events that have more than 5 occurrences since events that happen less than that are negligible. In addition, we will also know look at the column for PROPDMGEXP and CROPDMGEXP.

storm <- storm %>% group_by(EVTYPE) %>% filter(n()>=5) %>% ungroup
table(storm$PROPDMGEXP)

            0      B      K      M 
189113      1     29 328458   5550 
table(storm$CROPDMGEXP)

            B      K      M 
250601      4 271351   1195 

It seems like most of the estimates are in thousands (denoted by K), followed by millions (M) and some cases of billions (B), wow those are bad…

However, there are also many entries where there are no denotations under PROPDMGEXP and CROPDMGEXP. Does this mean that the corresponding entries under PROPDMG and CROPDMG are 0 (i.e. no economic damage). Let’s do a quick check on this.

# Segments out those rows with NULL entries in PROPDMGEXP, then check the corresponding PROPDMG entries if they are all equal to 0
all( (storm %>% filter(storm$PROPDMGEXP == ''))$PROPDMG == 0 )
[1] TRUE
# Do the same check for CROPDMGEXP and CROPDMG
all( (storm %>% filter(storm$CROPDMGEXP == ''))$CROPDMG == 0 )
[1] TRUE

Okay! This is good, now we can convert these in terms of thousands (K) without worrying.

The steps in this following chunk code will be:

  1. Convert B and M to numerical values in thousands (i.e. B will become 1,000,000 and M will be 1,000) for both PROPDMGEXP and CROPDMGEXP.

  2. Convert K to 1 (already in thousand) and those NULL values to 0.

  3. Multiply PROPDMGEXP and PROPDMG together to obtain a standardized numerical value that is in thousands. Do the same for CROPDMGEXP and CROPDMG.

  4. Combine these 2 columns into 1 so we can use this column to measure economical damage.

# Step 1 & 2
storm$PROPDMGEXP[storm$PROPDMGEXP == '' | storm$PROPDMGEXP == 0] <- 'N'
storm$PROPDMGEXP <- recode(storm$PROPDMGEXP, B = 1000000, M = 1000, K = 1, N = 0)
storm$CROPDMGEXP[storm$CROPDMGEXP == ''] <- 'N'
storm$CROPDMGEXP <- recode(storm$CROPDMGEXP, B = 1000000, M = 1000, K = 1, N = 0)
# Step 3 & 4
storm <- storm %>% mutate(PROP = PROPDMG*PROPDMGEXP, CROP = CROPDMG*CROPDMGEXP) %>% select(-PROPDMG, -PROPDMGEXP, -CROPDMG, -CROPDMGEXP) %>% mutate(DMG = PROP+CROP) %>% select(-PROP,-CROP)

Now that all of these cleaning and preprocessing of data has been completed, we are finally ready to move on to the analysis.

Results

In this analysis, we will look at the top 6 weather events that have occurred by total amount and average amount of economical damage caused. This will also be done similarly to gauge the effects on the population health.

Firstly, let’s look at the top 6 highest frequency of weather events.

storm %>% group_by(EVTYPE) %>% summarise(FREQ = n()) %>% arrange(desc(FREQ))

Effects of weather events on the economy

We should expect that the total amount of economical damage should some what follow the same order since the higher number of occurrences, the higher the chances of economical damage, thus increasing the total amount of economical damage.

storm %>% group_by(EVTYPE) %>% summarise(COST = sum(DMG)) %>% arrange(desc(COST))

This order of results isn’t the same as the number of occurrences! This proves that there are certain events, which may occur less frequently, and yet can cause a high amount of economical damage when they do happen! Just simply look at the event ‘HURRICANE/TYPHOON’, this isn’t even in the top 10 occurrences by frequency, and yet it is so deadly as it comes in second in economical damage ranking.

To put this into perspective, let’s visualize how often they occur and the total amount of economical damage caused in a bar chart. Let’s look at the top 6 events by economical damage.

storm %>% group_by(EVTYPE) %>% summarize(COST = sum(DMG), FREQ = n()) %>% 
    arrange(desc(COST)) %>% 
    slice(1:6) %>% 
    gather(VARIABLE, VALUE, c(COST,FREQ)) %>% 
    ggplot(aes(x=reorder(EVTYPE,-VALUE), y=VALUE, fill = EVTYPE)) + 
    geom_bar(stat='identity') + 
    facet_grid(VARIABLE ~ ., scales = 'free') + 
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(title = 'Distribution of economic costs and frequency of severe weather events',
         x = 'Weather Event', y = 'Frequency / Economic Cost ($k)') + 
    guides(fill = FALSE)

From the graph, it is apparent flooding and hurricane/typhoon are the deadliest in terms of economic damage whenever they occur. On the other hand, heavy rain/storms and blizzard/hail may occur a lot but they do not tend to cause so much damage whenever they do.

These are the top 6 events that have caused the most economical damage displayed together with the frequencies. Yet, if we truly wish to know how deadly a weather event can be whenever it occurs, it would be more accurate to look at the cost per frequency ratio.

storm %>% group_by(EVTYPE) %>% summarize(COST = sum(DMG), FREQ = n()) %>% mutate(RATIO = COST/FREQ) %>% arrange(desc(RATIO))

Well, it is not surprising to see hurricane/typhoon at the top of the list, yet a surprising entrant is Tsunami which comes in second. It is definitely one of the least occurring events, yet it does pose some serious damage to the economy. In addition, there are also other events that were not seen previously such as drought/dry and wild/forest fire.

Let’s not delve any deeper into this analysis, we shall now move on to explore the weather effects on population health in the same way.

Effects of weather events on population health

As we already know the number of occurrences for each weather event, let’s look at the number of fatalities and injuries for each weather event. In this analysis, we shall not separate the fatalities and injuries columns, let’s merge them together to use as the basis for this investigation.

storm %>% mutate(CASUALTIES = FATALITIES + INJURIES) %>% group_by(EVTYPE) %>% summarize(TOTALCAS = sum(CASUALTIES)) %>% arrange(desc(TOTALCAS))

We are now noticing a different order of weather events in terms of population risks. Not surprisingly tornado is one of them, yet now we noticed extreme weather conditions, lightning and heat as some of the new entrants.

Let’s plot a similar graph as above to see the top 6 distribution of the weather events in terms of its population effects and its frequency.

storm %>% mutate(CASUALTIES = FATALITIES + INJURIES) %>% 
    group_by(EVTYPE) %>% summarize(TOTCAS = sum(CASUALTIES), FREQ = n()) %>% 
    arrange(desc(TOTCAS)) %>% 
    slice(1:6) %>% 
    gather(VARIABLE, VALUE, c(TOTCAS,FREQ)) %>% 
    ggplot(aes(x=reorder(EVTYPE,-VALUE), y=VALUE, fill = EVTYPE)) + 
    geom_bar(stat='identity') + 
    facet_grid(VARIABLE ~ ., scales = 'free') + 
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(title = 'Distribution of casualties and frequency of severe weather events',
         x = 'Weather Event', y = 'Frequency / No. of Casualties') + 
    guides(fill = FALSE)

Similarly, we do not see similar distributions between these 2 graphs. Tornado, lightning, extreme weather conditions/occurrences and heat are truly deadly as they can pose really dangerous risks on the public whenever they happen.

Lastly, let’s look at the number of casualties per weather event occurrence ratio.

storm %>% mutate(CASUALTIES = FATALITIES + INJURIES) %>% group_by(EVTYPE) %>% summarize(TOTALCAS = sum(CASUALTIES), FREQ = n()) %>% mutate(RATIO = TOTALCAS/FREQ) %>% arrange(desc(RATIO))

Once again, we see new entrants that top the charts for ratio of number of casualties per occurrence. Hurricane/typhoon and tsunami are now the 2 weather events that cause the most number of casualties whenever they happen.

In addition, we have also noticed that these 2 weather events are the ones that caused the most economical damage whenever they occur as well. Hence, these 2 events are truly dangerous and deadly, and efforts should be put into researching and finding out ways to mitigate their effects.

LS0tCnRpdGxlOiAiRWZmZWN0cyBvZiBTdG9ybXMgb24gUG9wdWxhdGlvbiBIZWFsdGggYW5kIEVjb25vbXkgaW4gdGhlIFUuUy4iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyBTeXBub3NpcwoKSW4gdGhpcyByZXBvcnQsIHdlIHNlZWsgdG8gZXhhbWluZSB3aGljaCB0eXBlcyBvZiBldmVudHMgYXJlIG1vc3QgaGFybWZ1bCB0byBwb3B1bGF0aW9uIGhlYWx0aCBhbmQgZWNvbm9taWMgY29uc2VxdWVuY2VzIGZyb20gdGhlIGRhdGEsIHBhcnRpY3VsYXJseSBmcm9tIHllYXIgMTk5MCBvbndhcmRzLiBUbyBpbnZlc3RpZ2F0ZSB0aGlzLCB3ZSBvYnRhaW5lZCB0aGUgZGF0YSBmcm9tIHRoZSBOT0FBIFN0b3JtIERhdGFiYXNlIHdoaWNoIGNvbGxlY3RzIGNoYXJhY3RlcmlzdGljcyBvZiBtYWpvciBzdG9ybXMgYW5kIHdlYXRoZXIgZXZlbnRzIGluIHRoZSBVLlMuIGluY2x1ZGluZyB0eXBlcyBvZiBzdG9ybXMsIGVzdGltYXRlcyBvZiBhbnkgZmF0YWxpdGllcywgaW5qdXJpZXMsIGFuZCBwcm9wZXJ0eSBkYW1hZ2UgZnJvbSB0aGUgeWVhciAxOTUwIHRvIGVuZCBOb3ZlbWJlciAyMDExLgoKRnJvbSB0aGVzZSBkYXRhLCB3ZSBmb3VuZCBvdXQgdGhhdCBodXJyaWNhbmUvdHlwaG9vbiBhbmQgdHN1bmFtaSBldmVudHMgaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIG51bWJlciBvZiBjYXN1YWx0aWVzIGFuZCBlY29ub21pY2FsIGRhbWFnZSBwZXIgb2NjdXJyZW5jZS4KCiMgTG9hZGluZyBhbmQgUHJvY2Vzc2luZyB0aGUgUmF3IERhdGEKCkZyb20gdGhlIE5PQUEgU3Rvcm0gRGF0YWJhc2UsIHdlIG9idGFpbmVkIHRoZSBbZGF0YV0oaHR0cHM6Ly9kMzk2cXVzemE0MG9yYy5jbG91ZGZyb250Lm5ldC9yZXBkYXRhJTJGZGF0YSUyRlN0b3JtRGF0YS5jc3YuYnoyKSB0aGF0IG1vbml0b3JlZCB0aGUgbWFqb3Igc3Rvcm1zIGFuZCB3ZWF0aGVyIGV2ZW50cyBpbiB0aGUgVS5TLiBBZGRpdGlvbmFsIGRvY3VtZW50YXRpb24gb2YgdGhlIGRhdGFiYXNlIGlzIGF2YWlsYWJsZSB3aGljaCBjYW4gYmUgZm91bmQgYXQ6CgoqIE5hdGlvbmFsIFdlYXRoZXIgU2VydmljZSBbU3Rvcm0gRGF0YSBEb2N1bWVudGF0aW9uXShodHRwczovL2QzOTZxdXN6YTQwb3JjLmNsb3VkZnJvbnQubmV0L3JlcGRhdGElMkZwZWVyMl9kb2MlMkZwZDAxMDE2MDA1Y3Vyci5wZGYpCgoqIE5hdGlvbmFsIENsaW1hdGljIERhdGEgQ2VudGVyIFN0b3JtIEV2ZW50cyBbRkFRXShodHRwczovL2QzOTZxdXN6YTQwb3JjLmNsb3VkZnJvbnQubmV0L3JlcGRhdGElMkZwZWVyMl9kb2MlMkZOQ0RDJTIwU3Rvcm0lMjBFdmVudHMtRkFRJTIwUGFnZS5wZGYpCgoqVGhlIGFjY3VyYWN5IG9mIHRoaXMgYW5hbHlzaXMgd291bGQgaGVhdmlseSBkZXBlbmQgb24gdGhlIHByZXByb2Nlc3NpbmcgYW5kIGNsZWFuaW5nIG9mIHRoZSBkYXRhLiBUaGUgY2xlYW5lciBhbmQgcHJvY2Vzc2VkIHRoZSBkYXRhIGlzLCB0aGUgbW9yZSBhY2N1cmF0ZSB0aGUgYW5hbHlzaXMgYW5kIHJlc3VsdHMgd2lsbCBiZS4gSGVuY2UsIEkgd2lsbCBiZSBwdXR0aW5nIGVmZm9ydCBpbnRvIHRoaXMgc2VjdGlvbiwgc28gcGxlYXNlIGV4cGVjdCB0aGlzIHNlY3Rpb24gdG8gYmUgbGVuZ3RoeS4qCgojIyMgTG9hZGluZyB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzClRoZSBwYWNrYWdlcyB0aGF0IGFyZSB1c2VkIGZvciB0aGlzIGFuYWx5c2lzIHdpbGwgYmUgbG9hZGVkLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCiMjIyBSZWFkaW5nIHRoZSBkYXRhIGFuZCBleHBsb3JhdGlvbgoKQWZ0ZXIgdGhpcyBpcyBkb25lLCB3ZSB3aWxsIGxvYWQgdGhlIGRhdGFzZXQgdXNpbmcgdGhlIGBmcmVhZGAgZnVuY3Rpb24gZnJvbSB0aGUgYGRhdGEudGFibGVgIHBhY2thZ2Ugd2hpY2ggaXMgd2F5IGZhc3RlciB0aGFuIHRoZSBjb252ZW50aW9uYWwgbWV0aG9kLgpgYGB7cn0Kc3Rvcm0gPC0gZnJlYWQoInN0b3JtLmNzdiIpCmBgYAoKTmV4dCBzdGVwIHdvdWxkIGJlIHRvIGdldCBhIHF1aWNrIHNlbnNlIG9mIHRoZSBkYXRhLgoKYGBge3J9CmRpbShzdG9ybSkKaGVhZChzdG9ybSkKc3RyKHN0b3JtKQpgYGAKCiMjIyBDbGVhbmluZyBhbmQgcHJlcHJvY2Vzc2luZyBvZiBkYXRhCgpJbiB0aGlzIGFuYWx5c2lzLCBJIHdvdWxkIG9ubHkgKipleGFtaW5lIHRoZSBlZmZlY3RzIGZyb20gdGhlIHllYXIgMjAwMCBvbndhcmRzKiogKGkuZS4gZXZlbnRzIG9uIGFuZCBhZnRlciAwMS8wMS8yMDAwKSBiZWNhdXNlIHRoZSAqKmNvbnNlcXVlbmNlcyBvZiBzZXZlcmUgc3Rvcm0gZXZlbnRzIHRoYXQgaGFwcGVuZWQgd2F5IGVhcmxpZXIgbWF5IG5vdCBoYXZlIHRoZSBzYW1lIGltcGFjdCBhcyBpdCBkb2VzIG1hbnkgZGVjYWRlcyBsYXRlcioqLiBUaGlzIGlzIGR1ZSB0byBpbmZyYXN0cnVjdHVyZXMgYW5kIHByb3BlcnRpZXMgYXJlIG5vdyB3YXkgc3R1cmRpZXIgdGhhbiBiZWZvcmUsIHBlb3BsZSBhcmUgYWxzbyBtb3JlIGtub3dsZWRnZWFibGUgYW5kIHByZXBhcmVkIHRoYW4gYmVmb3JlLiBIZW5jZSwgaXQgaXMgdmVyeSBsaWtlbHkgdGhhdCB0aGUgYXZlcmFnZSBjb3N0IGFuZCBlZmZlY3Qgb2YgYSB0b3JuYWRvIG9mIGEgY2VydGFpbiBtYWduaXR1ZGUgb2Ygc3RyZW5ndGggaXMgbGVzc2VyIG5vdyB0aGFuIG1hbnkgZGVjYWRlcyBhZ28uCgpJdCB3b3VsZCB0aGVuLCBiZSBtb3JlIGFwcHJvcHJpYXRlLCB0byBvbmx5IHVzZSB0aGUgZGF0YSB0aGF0IGhhcHBlbmVkIGluIHRoZSBsYXN0IDE2IHllYXJzIChpLmUuIGZyb20gMjAwMCB0byBlbmQgMjAxMSkuIExldCdzIGZpbHRlciB0aGUgZGF0YSBhcHByb3ByaWF0ZWx5IGluIHRoZSBmb2xsb3dpbmcgY29kZSBiZWxvdy4KCmBgYHtyfQpzdG9ybSRCR05fREFURSA8LSB3aXRoKHN0b3JtLCBhcy5EYXRlKG1keV9obXMoQkdOX0RBVEUpKSkKc3Rvcm0gPC0gc3Vic2V0KHN0b3JtLCBCR05fREFURSA+PSAnMjAwMC0wMS0wMScpCmRpbShzdG9ybSkKYGBgCgpJdCBzZWVtcyBsaWtlIG9ubHkgYWJvdXQgMzgwayBvZiBlbnRyaWVzIGFyZSByZWNvcmRlZCBmcm9tIHllYXIgMTk1MCB0byAxOTk5ICg1MCB5ZWFycykuIEluIGNvbXBhcmlzb24sIHRoZXJlIGFyZSA1MjNrIG9mIGVudHJpZXMgZnJvbSAyMDEwIHRvIDIwMTEgKDEyIHllYXJzKSEgVGhpcyBpcyBoZWxwZnVsIGZvciB1cyBhcyBpdCB3b3VsZCBhbHNvIHJlbW92ZSBtYW55IG9sZCBlbnRyaWVzIHRoYXQgcHJvYmFibHkgd291bGRuJ3QgY29udGFpbiB1c2VmdWwgaW5mb3JtYXRpb24gaW4gdGhpcyBhbmFseXNpcy4gCgpOZXh0LCB3ZSB3b3VsZCBvbmx5IG5lZWQgY2VydGFpbiBjb2x1bW5zIG9mIHRoaXMgZGF0YXNldCBmb3IgdGhpcyBhbmFseXNpcywgc28gbGV0J3MgZXh0cmFjdCB0aG9zZSBvdXQuIEFmdGVyIGRvaW5nIHNvLCBsZXQncyBhbHNvIGhhdmUgYSBsb29rIGF0IHRoZSBldmVudCB0eXBlcyB0aGF0IGNhbiBoYXBwZW4uCgpgYGB7cn0Kc3Rvcm0gPC0gc3Rvcm0gJT4lIHNlbGVjdChFVlRZUEUsIEZBVEFMSVRJRVMsIElOSlVSSUVTLCBQUk9QRE1HLCBQUk9QRE1HRVhQLCBDUk9QRE1HLCBDUk9QRE1HRVhQKQpkYXRhLmZyYW1lKHRhYmxlKHN0b3JtJEVWVFlQRSkpCmBgYAoKV2Ugbm90aWNlZCB0aGF0IHRoZXJlIGFyZSBtYW55IGNhdGVnb3JpZXMgdGhhdCBjb3VsZCBiZWxvbmcgdG9nZXRoZXIsIHlldCBjbGFzc2ZpZWQgc2VwYXJhdGVseSBkdWUgdG86CgoqIFNpbmd1bGFyIHYucy4gUGx1cmFsIGZvcm0gKGUuZy4gJ1RodW5kZXJzdG9ybScgdi5zLiAnVGh1bmRlcnN0b3JtcycpCgoqIENhdGVnb3JpemVkIG1hZ25pdHVkZXMgKGUuZy4gJ1RTVE0gV2luZCAoRzQwKScgdi5zLiAnVFNUTSBXaW5kIChHNDUpJykKCiogRGlmZmVyZW50IG5hbWluZ3MgKGUuZy4gJ0hpZ2ggU3VyZicsICdIaWdoIFN1cmYgQWR2aXNvcmllcycsICdIaWdoIFN1cmYgQWR2aXNvcnknLCAnSGVhdnkgU3VyZicpCgpUaGUgbmV4dCBjaHVuayBvZiBjb2RlLCB3aWxsIHByb2NlZWQgdG8gY2xlYW4gdXAgYWxsIHRoZXNlIGlycmVndWxhcml0aWVzIHRoYXQgbWF5IGJlIHByZXNlbnQgYW5kIGdyb3VwIHVwIGFzIG11Y2ggYXMgcG9zc2libGUgdXNpbmcgcmVndWxhciBleHByZXNzaW9ucy4gU2luY2UgdGhlcmUgaXMgbm8gcXVpY2sgd2F5IHRvIGdvIGFib3V0IGRvaW5nIHRoaXMsIHRoaXMgcHJvY2VzcyBtYXkgYmUgJ21hbnVhbCcuCgpgYGB7cn0Kc3Rvcm0kRVZUWVBFW2dyZXBsKCdEVVNUJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ0RVU1QgU1RPUk0nCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ1VOVVNVQUxMWXxVTlNFQVNPTkFMfFVOU0VBU09OQUJMWXxBQk5PUk1BTExZfEVYVFJFTUV8RVhUUkVNRUxZfChFWENFU1NJVkUgSEVBVCl8KFZFUlkgV0FSTXxEUlkpfFJFQ09SRHxQUk9MT05HfChXQVJNIFdFQVRIRVIpJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ0VYVFJFTUUgQ09MRC9IT1QvV0VUL0RSWS9FVkVOVCcgCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ15EUll8XkRST1VHSFQnLCBzdG9ybSRFVlRZUEUpXSA8LSAnRFJPVUdIVC9EUlknCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ1NVUkZ8KEhJR0ggU0VBUyl8KEhJR0ggV0FURVIpfFNFSUNIRScsIHN0b3JtJEVWVFlQRSldIDwtICdISUdIIFNVUkYnCnN0b3JtJEVWVFlQRVtncmVwbCgnQVNUUk9OT01JQ0FMfChISUdIIFNVUkYpfENVUlJFTlR8Uk9HVUV8KFJPVUdIIFNFQVMpJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ0xPVy9ISUdIIFRJREUvU1VSRi9DVVJSRU5UJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdUU1RNIFdJTkR8KFRIVU5ERVJTVE9STSBXSU5EKScsIHN0b3JtJEVWVFlQRSldIDwtICdUSFVOREVSU1RPUk0gV0lORCcKc3Rvcm0kRVZUWVBFW2dyZXBsKCdUSFVOREVSU1RPUk0nLCBzdG9ybSRFVlRZUEUpXSA8LSAnVEhVTkRFUlNUT1JNIFdJTkQnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ0ZMT09EfEZMRHxEUk9XTklORycsIHN0b3JtJEVWVFlQRSldIDwtICdGTE9PRElORycKCnN0b3JtJEVWVFlQRVtncmVwbCgnU05PVycsIHN0b3JtJEVWVFlQRSldIDwtICdTTk9XJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdeQ09MRCcsIHN0b3JtJEVWVFlQRSldIDwtICdDT0xEJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdGUkVFWnxGUk9TVCcsIHN0b3JtJEVWVFlQRSldIDwtICdGUkVFWkUvRlJPU1QnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ0ZPR3xTTU9LRScsIHN0b3JtJEVWVFlQRSldIDwtICdGT0cvU01PS0UnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ15XSU5URVJ8V0lOVFJZJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ1dJTlRFUiBXRUFUSEVSJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdeV0lORHwoU1RST05HIFdJTkQpfFdORHxHVVNUWXxHUkFESUVOVCcsIHN0b3JtJEVWVFlQRSldIDwtICdXSU5EJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdIQUlMfChJQ0UgU1RPUk0pfFNMRUVUfEJMSVpaQVJEfEdMQVpFJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ0JMSVpaQVJEL0hBSUwnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ1dJTER8KEJSVVNIIEZJUkUpJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ1dJTEQvRk9SRVNUIEZJUkUnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ1RPUk5BRE98V0hJUkxEV0lORCcsIHN0b3JtJEVWVFlQRSldIDwtICdUT1JOQURPJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCcoQkxBQ0sgSUNFKXwoSUNFIE9OIFJPQUQpfChJQ1kgUk9BRCl8KFBBVENIWSBJQ0UpJywgc3Rvcm0kRVZUWVBFKV0gPC0gJ0lDWSBST0FEJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdGVU5ORUx8V0FMTCcsIHN0b3JtJEVWVFlQRSldIDwtICdGVU5ORUwgQ0xPVURTJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdWT0xDQU4nLCBzdG9ybSRFVlRZUEUpXSA8LSAnVk9MQ0FOSUMgQVNIJwoKc3Rvcm0kRVZUWVBFW2dyZXBsKCdNVUQnLCBzdG9ybSRFVlRZUEUpXSA8LSAnTVVEU0xJREUnCgpzdG9ybSRFVlRZUEVbZ3JlcGwoJ0hVUlJJQ0FORScsIHN0b3JtJEVWVFlQRSldIDwtICdIVVJSSUNBTkUvVFlQSE9PTicKCnN0b3JtJEVWVFlQRVtncmVwbCgnTEFORHxBVkFMQU5DSEUnLCBzdG9ybSRFVlRZUEUpXSA8LSAnTEFORFNMSURFL0FWQUxBTkNIRScKCnN0b3JtJEVWVFlQRVtncmVwbCgnU1RPUk18U1VSR0V8REVQUkVTU0lPTnxSQUlOfFBSRUNJUElUQVRJT04nLCBzdG9ybSRFVlRZUEUpXSA8LSAnSEVBVlkgUkFJTi9TVE9STVMnCmBgYAoKV2hldyEgQWZ0ZXIgYWxsIHRoYXQgaGFyZHdvcmssIG5vdyBhbGwgdGhhdCdzIGxlZnQgaXMgdG8gZG8gYSBzaW1wbGUgcXVpY2sgZmlsdGVyIHRvIGtlZXAgdGhvc2UgZXZlbnRzIHRoYXQgaGF2ZSBtb3JlIHRoYW4gNSBvY2N1cnJlbmNlcyBzaW5jZSBldmVudHMgdGhhdCBoYXBwZW4gbGVzcyB0aGFuIHRoYXQgYXJlIG5lZ2xpZ2libGUuIEluIGFkZGl0aW9uLCB3ZSB3aWxsIGFsc28ga25vdyBsb29rIGF0IHRoZSBjb2x1bW4gZm9yIGBQUk9QRE1HRVhQYCBhbmQgYENST1BETUdFWFBgLgoKYGBge3J9CnN0b3JtIDwtIHN0b3JtICU+JSBncm91cF9ieShFVlRZUEUpICU+JSBmaWx0ZXIobigpPj01KSAlPiUgdW5ncm91cAoKdGFibGUoc3Rvcm0kUFJPUERNR0VYUCkKCnRhYmxlKHN0b3JtJENST1BETUdFWFApCmBgYAoKSXQgc2VlbXMgbGlrZSBtb3N0IG9mIHRoZSBlc3RpbWF0ZXMgYXJlIGluIHRob3VzYW5kcyAoZGVub3RlZCBieSBLKSwgZm9sbG93ZWQgYnkgbWlsbGlvbnMgKE0pIGFuZCBzb21lIGNhc2VzIG9mIGJpbGxpb25zIChCKSwgd293IHRob3NlIGFyZSBiYWQuLi4KCkhvd2V2ZXIsIHRoZXJlIGFyZSBhbHNvIG1hbnkgZW50cmllcyB3aGVyZSB0aGVyZSBhcmUgbm8gZGVub3RhdGlvbnMgdW5kZXIgYFBST1BETUdFWFBgIGFuZCBgQ1JPUERNR0VYUGAuIERvZXMgdGhpcyBtZWFuIHRoYXQgdGhlIGNvcnJlc3BvbmRpbmcgZW50cmllcyB1bmRlciBgUFJPUERNR2AgYW5kIGBDUk9QRE1HYCBhcmUgMCAoaS5lLiBubyBlY29ub21pYyBkYW1hZ2UpLiBMZXQncyBkbyBhIHF1aWNrIGNoZWNrIG9uIHRoaXMuCgpgYGB7cn0KIyBTZWdtZW50cyBvdXQgdGhvc2Ugcm93cyB3aXRoIE5VTEwgZW50cmllcyBpbiBQUk9QRE1HRVhQLCB0aGVuIGNoZWNrIHRoZSBjb3JyZXNwb25kaW5nIFBST1BETUcgZW50cmllcyBpZiB0aGV5IGFyZSBhbGwgZXF1YWwgdG8gMAphbGwoIChzdG9ybSAlPiUgZmlsdGVyKHN0b3JtJFBST1BETUdFWFAgPT0gJycpKSRQUk9QRE1HID09IDAgKQoKIyBEbyB0aGUgc2FtZSBjaGVjayBmb3IgQ1JPUERNR0VYUCBhbmQgQ1JPUERNRwphbGwoIChzdG9ybSAlPiUgZmlsdGVyKHN0b3JtJENST1BETUdFWFAgPT0gJycpKSRDUk9QRE1HID09IDAgKQpgYGAKCk9rYXkhIFRoaXMgaXMgZ29vZCwgbm93IHdlIGNhbiBjb252ZXJ0IHRoZXNlIGluIHRlcm1zIG9mIHRob3VzYW5kcyAoSykgd2l0aG91dCB3b3JyeWluZy4KClRoZSBzdGVwcyBpbiB0aGlzIGZvbGxvd2luZyBjaHVuayBjb2RlIHdpbGwgYmU6CgoxLiBDb252ZXJ0IEIgYW5kIE0gdG8gbnVtZXJpY2FsIHZhbHVlcyBpbiB0aG91c2FuZHMgKGkuZS4gQiB3aWxsIGJlY29tZSAxLDAwMCwwMDAgYW5kIE0gd2lsbCBiZSAxLDAwMCkgZm9yIGJvdGggYFBST1BETUdFWFBgIGFuZCBgQ1JPUERNR0VYUGAuCgoyLiBDb252ZXJ0IEsgdG8gMSAoYWxyZWFkeSBpbiB0aG91c2FuZCkgYW5kIHRob3NlIE5VTEwgdmFsdWVzIHRvIDAuCgozLiBNdWx0aXBseSBgUFJPUERNR0VYUGAgYW5kIGBQUk9QRE1HYCB0b2dldGhlciB0byBvYnRhaW4gYSBzdGFuZGFyZGl6ZWQgbnVtZXJpY2FsIHZhbHVlIHRoYXQgaXMgaW4gdGhvdXNhbmRzLiBEbyB0aGUgc2FtZSBmb3IgYENST1BETUdFWFBgIGFuZCBgQ1JPUERNR2AuCgo0LiBDb21iaW5lIHRoZXNlIDIgY29sdW1ucyBpbnRvIDEgc28gd2UgY2FuIHVzZSB0aGlzIGNvbHVtbiB0byBtZWFzdXJlIGVjb25vbWljYWwgZGFtYWdlLgoKCmBgYHtyfQojIFN0ZXAgMSAmIDIKc3Rvcm0kUFJPUERNR0VYUFtzdG9ybSRQUk9QRE1HRVhQID09ICcnIHwgc3Rvcm0kUFJPUERNR0VYUCA9PSAwXSA8LSAnTicKc3Rvcm0kUFJPUERNR0VYUCA8LSByZWNvZGUoc3Rvcm0kUFJPUERNR0VYUCwgQiA9IDEwMDAwMDAsIE0gPSAxMDAwLCBLID0gMSwgTiA9IDApCgpzdG9ybSRDUk9QRE1HRVhQW3N0b3JtJENST1BETUdFWFAgPT0gJyddIDwtICdOJwpzdG9ybSRDUk9QRE1HRVhQIDwtIHJlY29kZShzdG9ybSRDUk9QRE1HRVhQLCBCID0gMTAwMDAwMCwgTSA9IDEwMDAsIEsgPSAxLCBOID0gMCkKCiMgU3RlcCAzICYgNApzdG9ybSA8LSBzdG9ybSAlPiUgbXV0YXRlKFBST1AgPSBQUk9QRE1HKlBST1BETUdFWFAsIENST1AgPSBDUk9QRE1HKkNST1BETUdFWFApICU+JSBzZWxlY3QoLVBST1BETUcsIC1QUk9QRE1HRVhQLCAtQ1JPUERNRywgLUNST1BETUdFWFApICU+JSBtdXRhdGUoRE1HID0gUFJPUCtDUk9QKSAlPiUgc2VsZWN0KC1QUk9QLC1DUk9QKQpgYGAKCk5vdyB0aGF0IGFsbCBvZiB0aGVzZSBjbGVhbmluZyBhbmQgcHJlcHJvY2Vzc2luZyBvZiBkYXRhIGhhcyBiZWVuIGNvbXBsZXRlZCwgd2UgYXJlIGZpbmFsbHkgcmVhZHkgdG8gbW92ZSBvbiB0byB0aGUgYW5hbHlzaXMuCgojIFJlc3VsdHMKCkluIHRoaXMgYW5hbHlzaXMsIHdlIHdpbGwgbG9vayBhdCB0aGUgdG9wIDYgd2VhdGhlciBldmVudHMgdGhhdCBoYXZlIG9jY3VycmVkIGJ5IHRvdGFsIGFtb3VudCBhbmQgYXZlcmFnZSBhbW91bnQgb2YgZWNvbm9taWNhbCBkYW1hZ2UgY2F1c2VkLiBUaGlzIHdpbGwgYWxzbyBiZSBkb25lIHNpbWlsYXJseSB0byBnYXVnZSB0aGUgZWZmZWN0cyBvbiB0aGUgcG9wdWxhdGlvbiBoZWFsdGguIAoKRmlyc3RseSwgbGV0J3MgbG9vayBhdCB0aGUgdG9wIDYgaGlnaGVzdCBmcmVxdWVuY3kgb2Ygd2VhdGhlciBldmVudHMuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Kc3Rvcm0gJT4lIGdyb3VwX2J5KEVWVFlQRSkgJT4lIHN1bW1hcmlzZShGUkVRID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKEZSRVEpKQpgYGAKCiMjIyBFZmZlY3RzIG9mIHdlYXRoZXIgZXZlbnRzIG9uIHRoZSBlY29ub215CgpXZSBzaG91bGQgZXhwZWN0IHRoYXQgdGhlIHRvdGFsIGFtb3VudCBvZiBlY29ub21pY2FsIGRhbWFnZSBzaG91bGQgc29tZSB3aGF0IGZvbGxvdyB0aGUgc2FtZSBvcmRlciBzaW5jZSB0aGUgaGlnaGVyIG51bWJlciBvZiBvY2N1cnJlbmNlcywgdGhlIGhpZ2hlciB0aGUgY2hhbmNlcyBvZiBlY29ub21pY2FsIGRhbWFnZSwgdGh1cyBpbmNyZWFzaW5nIHRoZSB0b3RhbCBhbW91bnQgb2YgZWNvbm9taWNhbCBkYW1hZ2UuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Kc3Rvcm0gJT4lIGdyb3VwX2J5KEVWVFlQRSkgJT4lIHN1bW1hcmlzZShDT1NUID0gc3VtKERNRykpICU+JSBhcnJhbmdlKGRlc2MoQ09TVCkpCmBgYAoKVGhpcyBvcmRlciBvZiByZXN1bHRzIGlzbid0IHRoZSBzYW1lIGFzIHRoZSBudW1iZXIgb2Ygb2NjdXJyZW5jZXMhICoqVGhpcyBwcm92ZXMgdGhhdCB0aGVyZSBhcmUgY2VydGFpbiBldmVudHMsIHdoaWNoIG1heSBvY2N1ciBsZXNzIGZyZXF1ZW50bHksIGFuZCB5ZXQgY2FuIGNhdXNlIGEgaGlnaCBhbW91bnQgb2YgZWNvbm9taWNhbCBkYW1hZ2Ugd2hlbiB0aGV5IGRvIGhhcHBlbiEqKiBKdXN0IHNpbXBseSBsb29rIGF0IHRoZSBldmVudCAnSFVSUklDQU5FL1RZUEhPT04nLCB0aGlzIGlzbid0IGV2ZW4gaW4gdGhlIHRvcCAxMCBvY2N1cnJlbmNlcyBieSBmcmVxdWVuY3ksIGFuZCB5ZXQgaXQgaXMgc28gZGVhZGx5IGFzIGl0IGNvbWVzIGluIHNlY29uZCBpbiBlY29ub21pY2FsIGRhbWFnZSByYW5raW5nLgoKVG8gcHV0IHRoaXMgaW50byBwZXJzcGVjdGl2ZSwgbGV0J3MgdmlzdWFsaXplIGhvdyBvZnRlbiB0aGV5IG9jY3VyIGFuZCB0aGUgdG90YWwgYW1vdW50IG9mIGVjb25vbWljYWwgZGFtYWdlIGNhdXNlZCBpbiBhIGJhciBjaGFydC4gTGV0J3MgbG9vayBhdCB0aGUgdG9wIDYgZXZlbnRzIGJ5IGVjb25vbWljYWwgZGFtYWdlLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc3Rvcm0gJT4lIGdyb3VwX2J5KEVWVFlQRSkgJT4lIHN1bW1hcml6ZShDT1NUID0gc3VtKERNRyksIEZSRVEgPSBuKCkpICU+JSAKICAgIGFycmFuZ2UoZGVzYyhDT1NUKSkgJT4lIAogICAgc2xpY2UoMTo2KSAlPiUgCiAgICBnYXRoZXIoVkFSSUFCTEUsIFZBTFVFLCBjKENPU1QsRlJFUSkpICU+JSAKICAgIGdncGxvdChhZXMoeD1yZW9yZGVyKEVWVFlQRSwtVkFMVUUpLCB5PVZBTFVFLCBmaWxsID0gRVZUWVBFKSkgKyAKICAgIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKyAKICAgIGZhY2V0X2dyaWQoVkFSSUFCTEUgfiAuLCBzY2FsZXMgPSAnZnJlZScpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBlY29ub21pYyBjb3N0cyBhbmQgZnJlcXVlbmN5IG9mIHNldmVyZSB3ZWF0aGVyIGV2ZW50cycsCiAgICAgICAgIHggPSAnV2VhdGhlciBFdmVudCcsIHkgPSAnRnJlcXVlbmN5IC8gRWNvbm9taWMgQ29zdCAoJGspJykgKyAKICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpCmBgYAoKRnJvbSB0aGUgZ3JhcGgsIGl0IGlzIGFwcGFyZW50IGZsb29kaW5nIGFuZCBodXJyaWNhbmUvdHlwaG9vbiBhcmUgdGhlIGRlYWRsaWVzdCBpbiB0ZXJtcyBvZiBlY29ub21pYyBkYW1hZ2Ugd2hlbmV2ZXIgdGhleSBvY2N1ci4gT24gdGhlIG90aGVyIGhhbmQsIGhlYXZ5IHJhaW4vc3Rvcm1zIGFuZCBibGl6emFyZC9oYWlsIG1heSBvY2N1ciBhIGxvdCBidXQgdGhleSBkbyBub3QgdGVuZCB0byBjYXVzZSBzbyBtdWNoIGRhbWFnZSB3aGVuZXZlciB0aGV5IGRvLgoKVGhlc2UgYXJlIHRoZSB0b3AgNiBldmVudHMgdGhhdCBoYXZlIGNhdXNlZCB0aGUgbW9zdCBlY29ub21pY2FsIGRhbWFnZSBkaXNwbGF5ZWQgdG9nZXRoZXIgd2l0aCB0aGUgZnJlcXVlbmNpZXMuIFlldCwgaWYgd2UgdHJ1bHkgd2lzaCB0byBrbm93IGhvdyBkZWFkbHkgYSB3ZWF0aGVyIGV2ZW50IGNhbiBiZSB3aGVuZXZlciBpdCBvY2N1cnMsIGl0IHdvdWxkIGJlIG1vcmUgYWNjdXJhdGUgdG8gbG9vayBhdCB0aGUgY29zdCBwZXIgZnJlcXVlbmN5IHJhdGlvLiAKCmBgYHtyfQpzdG9ybSAlPiUgZ3JvdXBfYnkoRVZUWVBFKSAlPiUgc3VtbWFyaXplKENPU1QgPSBzdW0oRE1HKSwgRlJFUSA9IG4oKSkgJT4lIG11dGF0ZShSQVRJTyA9IENPU1QvRlJFUSkgJT4lIGFycmFuZ2UoZGVzYyhSQVRJTykpCmBgYAoKV2VsbCwgaXQgaXMgbm90IHN1cnByaXNpbmcgdG8gc2VlIGh1cnJpY2FuZS90eXBob29uIGF0IHRoZSB0b3Agb2YgdGhlIGxpc3QsIHlldCBhIHN1cnByaXNpbmcgZW50cmFudCBpcyBUc3VuYW1pIHdoaWNoIGNvbWVzIGluIHNlY29uZC4gSXQgaXMgZGVmaW5pdGVseSBvbmUgb2YgdGhlIGxlYXN0IG9jY3VycmluZyBldmVudHMsIHlldCBpdCBkb2VzIHBvc2Ugc29tZSBzZXJpb3VzIGRhbWFnZSB0byB0aGUgZWNvbm9teS4gSW4gYWRkaXRpb24sIHRoZXJlIGFyZSBhbHNvIG90aGVyIGV2ZW50cyB0aGF0IHdlcmUgbm90IHNlZW4gcHJldmlvdXNseSBzdWNoIGFzIGRyb3VnaHQvZHJ5IGFuZCB3aWxkL2ZvcmVzdCBmaXJlLgoKTGV0J3Mgbm90IGRlbHZlIGFueSBkZWVwZXIgaW50byB0aGlzIGFuYWx5c2lzLCB3ZSBzaGFsbCBub3cgbW92ZSBvbiB0byBleHBsb3JlIHRoZSB3ZWF0aGVyIGVmZmVjdHMgb24gcG9wdWxhdGlvbiBoZWFsdGggaW4gdGhlIHNhbWUgd2F5LgoKIyMjIEVmZmVjdHMgb2Ygd2VhdGhlciBldmVudHMgb24gcG9wdWxhdGlvbiBoZWFsdGgKCkFzIHdlIGFscmVhZHkga25vdyB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzIGZvciBlYWNoIHdlYXRoZXIgZXZlbnQsIGxldCdzIGxvb2sgYXQgdGhlIG51bWJlciBvZiBmYXRhbGl0aWVzIGFuZCBpbmp1cmllcyBmb3IgZWFjaCB3ZWF0aGVyIGV2ZW50LiBJbiB0aGlzIGFuYWx5c2lzLCB3ZSBzaGFsbCBub3Qgc2VwYXJhdGUgdGhlIGZhdGFsaXRpZXMgYW5kIGluanVyaWVzIGNvbHVtbnMsIGxldCdzIG1lcmdlIHRoZW0gdG9nZXRoZXIgdG8gdXNlIGFzIHRoZSBiYXNpcyBmb3IgdGhpcyBpbnZlc3RpZ2F0aW9uLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc3Rvcm0gJT4lIG11dGF0ZShDQVNVQUxUSUVTID0gRkFUQUxJVElFUyArIElOSlVSSUVTKSAlPiUgZ3JvdXBfYnkoRVZUWVBFKSAlPiUgc3VtbWFyaXplKFRPVEFMQ0FTID0gc3VtKENBU1VBTFRJRVMpKSAlPiUgYXJyYW5nZShkZXNjKFRPVEFMQ0FTKSkKYGBgCgpXZSBhcmUgbm93IG5vdGljaW5nIGEgZGlmZmVyZW50IG9yZGVyIG9mIHdlYXRoZXIgZXZlbnRzIGluIHRlcm1zIG9mIHBvcHVsYXRpb24gcmlza3MuIE5vdCBzdXJwcmlzaW5nbHkgdG9ybmFkbyBpcyBvbmUgb2YgdGhlbSwgeWV0IG5vdyB3ZSBub3RpY2VkIGV4dHJlbWUgd2VhdGhlciBjb25kaXRpb25zLCBsaWdodG5pbmcgYW5kIGhlYXQgYXMgc29tZSBvZiB0aGUgbmV3IGVudHJhbnRzLgoKTGV0J3MgcGxvdCBhIHNpbWlsYXIgZ3JhcGggYXMgYWJvdmUgdG8gc2VlIHRoZSB0b3AgNiBkaXN0cmlidXRpb24gb2YgdGhlIHdlYXRoZXIgZXZlbnRzIGluIHRlcm1zIG9mIGl0cyBwb3B1bGF0aW9uIGVmZmVjdHMgYW5kIGl0cyBmcmVxdWVuY3kuIAoKYGBge3J9CnN0b3JtICU+JSBtdXRhdGUoQ0FTVUFMVElFUyA9IEZBVEFMSVRJRVMgKyBJTkpVUklFUykgJT4lIAogICAgZ3JvdXBfYnkoRVZUWVBFKSAlPiUgc3VtbWFyaXplKFRPVENBUyA9IHN1bShDQVNVQUxUSUVTKSwgRlJFUSA9IG4oKSkgJT4lIAogICAgYXJyYW5nZShkZXNjKFRPVENBUykpICU+JSAKICAgIHNsaWNlKDE6NikgJT4lIAogICAgZ2F0aGVyKFZBUklBQkxFLCBWQUxVRSwgYyhUT1RDQVMsRlJFUSkpICU+JSAKICAgIGdncGxvdChhZXMoeD1yZW9yZGVyKEVWVFlQRSwtVkFMVUUpLCB5PVZBTFVFLCBmaWxsID0gRVZUWVBFKSkgKyAKICAgIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKyAKICAgIGZhY2V0X2dyaWQoVkFSSUFCTEUgfiAuLCBzY2FsZXMgPSAnZnJlZScpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBjYXN1YWx0aWVzIGFuZCBmcmVxdWVuY3kgb2Ygc2V2ZXJlIHdlYXRoZXIgZXZlbnRzJywKICAgICAgICAgeCA9ICdXZWF0aGVyIEV2ZW50JywgeSA9ICdGcmVxdWVuY3kgLyBOby4gb2YgQ2FzdWFsdGllcycpICsgCiAgICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKClNpbWlsYXJseSwgd2UgZG8gbm90IHNlZSBzaW1pbGFyIGRpc3RyaWJ1dGlvbnMgYmV0d2VlbiB0aGVzZSAyIGdyYXBocy4gVG9ybmFkbywgbGlnaHRuaW5nLCBleHRyZW1lIHdlYXRoZXIgY29uZGl0aW9ucy9vY2N1cnJlbmNlcyBhbmQgaGVhdCBhcmUgdHJ1bHkgZGVhZGx5IGFzIHRoZXkgY2FuIHBvc2UgcmVhbGx5IGRhbmdlcm91cyByaXNrcyBvbiB0aGUgcHVibGljIHdoZW5ldmVyIHRoZXkgaGFwcGVuLgoKTGFzdGx5LCBsZXQncyBsb29rIGF0IHRoZSBudW1iZXIgb2YgY2FzdWFsdGllcyBwZXIgd2VhdGhlciBldmVudCBvY2N1cnJlbmNlIHJhdGlvLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc3Rvcm0gJT4lIG11dGF0ZShDQVNVQUxUSUVTID0gRkFUQUxJVElFUyArIElOSlVSSUVTKSAlPiUgZ3JvdXBfYnkoRVZUWVBFKSAlPiUgc3VtbWFyaXplKFRPVEFMQ0FTID0gc3VtKENBU1VBTFRJRVMpLCBGUkVRID0gbigpKSAlPiUgbXV0YXRlKFJBVElPID0gVE9UQUxDQVMvRlJFUSkgJT4lIGFycmFuZ2UoZGVzYyhSQVRJTykpCmBgYAoKT25jZSBhZ2Fpbiwgd2Ugc2VlIG5ldyBlbnRyYW50cyB0aGF0IHRvcCB0aGUgY2hhcnRzIGZvciByYXRpbyBvZiBudW1iZXIgb2YgY2FzdWFsdGllcyBwZXIgb2NjdXJyZW5jZS4gSHVycmljYW5lL3R5cGhvb24gYW5kIHRzdW5hbWkgYXJlIG5vdyB0aGUgMiB3ZWF0aGVyIGV2ZW50cyB0aGF0IGNhdXNlIHRoZSBtb3N0IG51bWJlciBvZiBjYXN1YWx0aWVzIHdoZW5ldmVyIHRoZXkgaGFwcGVuLgoKCioqSW4gYWRkaXRpb24sIHdlIGhhdmUgYWxzbyBub3RpY2VkIHRoYXQgdGhlc2UgMiB3ZWF0aGVyIGV2ZW50cyBhcmUgdGhlIG9uZXMgdGhhdCBjYXVzZWQgdGhlIG1vc3QgZWNvbm9taWNhbCBkYW1hZ2Ugd2hlbmV2ZXIgdGhleSBvY2N1ciBhcyB3ZWxsLiBIZW5jZSwgdGhlc2UgMiBldmVudHMgYXJlIHRydWx5IGRhbmdlcm91cyBhbmQgZGVhZGx5LCBhbmQgZWZmb3J0cyBzaG91bGQgYmUgcHV0IGludG8gcmVzZWFyY2hpbmcgYW5kIGZpbmRpbmcgb3V0IHdheXMgdG8gbWl0aWdhdGUgdGhlaXIgZWZmZWN0cy4qKg==