To start: import the trusty tidyverse package and the AirBnB data.

library(tidyverse)
library(ggmap)
library(RColorBrewer)
setwd("C:/Users/monica/Documents/Courses/Q7_Fall2018/GEOG 208/week4_project")
untidy <- read_csv("listings.csv")
Parsed with column specification:
cols(
  id = col_integer(),
  name = col_character(),
  host_id = col_integer(),
  host_name = col_character(),
  neighbourhood_group = col_character(),
  neighbourhood = col_character(),
  latitude = col_double(),
  longitude = col_double(),
  room_type = col_character(),
  price = col_integer(),
  minimum_nights = col_integer(),
  number_of_reviews = col_integer(),
  last_review = col_date(format = ""),
  reviews_per_month = col_double(),
  calculated_host_listings_count = col_integer(),
  availability_365 = col_integer()
)
head(untidy)
# A tibble: 6 x 16
     id name  host_id host_name neighbourhood_g~ neighbourhood latitude longitude room_type price minimum_nights
  <int> <chr>   <int> <chr>     <chr>            <chr>            <dbl>     <dbl> <chr>     <int>          <int>
1   109 Amaz~     521 Paolo     <NA>             Culver City       34.0     -118. Entire h~   122              7
2   344 Fami~     767 Melissa   <NA>             Burbank           34.2     -118. Entire h~   168              2
3 25445 Down~  105868 Namhau    <NA>             Culver City       34.0     -118. Private ~    95              1
4  2404 dele~    2633 Jjjj      <NA>             Del Rey           34.0     -118. Shared r~    85              1
5 25670 Char~  107370 Sandra    <NA>             West Los Ang~     34.0     -118. Entire h~    90              3
6 25672 Mode~  107473 Claudia   <NA>             East Hollywo~     34.1     -118. Entire h~   130              2
# ... with 5 more variables: number_of_reviews <int>, last_review <date>, reviews_per_month <dbl>,
#   calculated_host_listings_count <int>, availability_365 <int>
sapply(untidy, function(x) length(unique(x)))
                            id                           name                        host_id 
                         43763                          42918                          26730 
                     host_name            neighbourhood_group                  neighbourhood 
                          9053                              1                            260 
                      latitude                      longitude                      room_type 
                         43763                          43763                              3 
                         price                 minimum_nights              number_of_reviews 
                           836                             92                            424 
                   last_review              reviews_per_month calculated_host_listings_count 
                          1366                           1042                             49 
              availability_365 
                           366 

This doesn’t actually look too messy! I’m going to look at the price variable a little more closely, since I think I’ll be using it a lot.

summary(untidy$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0    69.0   105.0   194.2   180.0 25000.0 

That’s quite a range of values! The 25,000 seems like a real outlier, and I don’t really want to look at listings with a price of 0 USD.

airbnb_data <- untidy %>% filter(price >= 10)
summary(airbnb_data$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   10.0    69.0   105.0   194.3   180.0 25000.0 

1. Listings by Neighborhood

I want to start off by mapping the price and number of listings by neighborhood, faceted by room type. We’ll need a new dataset that includes the average price and number of listings of each room type, as well as produce a new set of coordinates for each neighborhood. The quickest and not-so-accurate way that I am going to do this is by simply averaging the latitudes and longitudes of the listings in each neighborhood. Lastly, I’ll arrange the table so that the higher values will be drawn last.

nbhood_mean <- airbnb_data %>% 
  group_by(neighbourhood, room_type) %>% 
  summarise(
    latitude = mean(latitude, na.rm = TRUE),
    longitude = mean(longitude, na.rm = TRUE),
    number_of_reviews = sum(number_of_reviews, na.rm = TRUE),
    last_review = mean(last_review, na.rm = TRUE),
    reviews_per_month = mean(reviews_per_month, na.rm = TRUE),
    listings = n(),
    price = mean(price, na.rm = TRUE)) %>%
  ungroup() %>%
  arrange(price)
head(nbhood_mean)
# A tibble: 6 x 9
  neighbourhood    room_type   latitude longitude number_of_reviews last_review reviews_per_month listings price
  <chr>            <chr>          <dbl>     <dbl>             <int> <date>                  <dbl>    <int> <dbl>
1 La Habra Heights Shared room     34.0     -118.                 0 NA                     NaN           1  10  
2 Green Meadows    Shared room     33.9     -118.                32 2018-08-29               3.29        3  18.7
3 Cudahy           Shared room     34.0     -118.                 0 NA                     NaN           1  20  
4 Pico-Union       Shared room     34.0     -118.              2215 2018-05-01               1.12      177  21.3
5 Monrovia         Shared room     34.1     -118.                 3 2016-07-21               0.11        1  22  
6 Watts            Shared room     33.9     -118.               165 2018-09-18               2.00       17  23.4

Next we’ll set the extent of the map…by liberally borrowing this snippet from r-bloggers.com:

LAheight <- max(airbnb_data$latitude) - min(airbnb_data$latitude)
LAwidth <- max(airbnb_data$longitude) - min(airbnb_data$longitude)
LA_borders <- c(bottom  = min(airbnb_data$latitude)  - 0.05 * LAheight, 
                 top     = max(airbnb_data$latitude) + 0.05 * LAheight,
                 left    = min(airbnb_data$longitude) - 0.05 * LAwidth,
                 right   = max(airbnb_data$longitude) + 0.05 * LAwidth)

I don’t want any labels cluttering up the background, so I’m using the “toner-background” Stamen Map:

LA_map <- get_stamenmap(LA_borders, zoom = 8, maptype = "toner-background")

And now the maps! I don’t think it’s necessary to show the x-/y-axis labels or titles, so I’ll remove those.

ggmap(LA_map) +
  geom_point(nbhood_mean, mapping = aes(x = longitude, y = latitude, color = price, size = listings)) +
  scale_color_distiller(palette = "RdYlBu", direction = -1, trans = "log10", name = "Price (USD)") +
  scale_size(breaks = c(0,1,10,100,1000), labels = c(">0",">=1",">=10",">=100",">=1000"), range = c(1,9), name = "No. of Listings") +
  theme(panel.background = element_rect(fill = "white")) +
  facet_wrap(. ~ room_type) +
  labs(title = "Listings by Neighborhood", x = "", y = "") +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        plot.title=element_text(vjust = 4, face = "bold"), 
        strip.text.x = element_text(size = 10.5, vjust = 3, face = "bold"),
        strip.background = element_rect(fill = FALSE))

As we might have expected, the most affordable options are shared rooms in Downtown/Central LA. The priciest listings are on the Westside and in the Hollywood Hills, where there are fewer shared room options.


2. East-West Distribution of Listings…aka, the Gap Instinct?

There seem to be so many listings on the Westside…and more expensive ones at that. Is there an uneven distribution of listings in LA, from East to West? Is there…a GAP???

ggplot(airbnb_data, aes(x = longitude, fill = room_type)) +
  geom_histogram(bins = 40) +
  scale_fill_manual(values = c("#efa35c", "#4ab8b8", "#1b3764"), name = "Room Type") +
  labs(title = "East-West Distribution of Listings", x = "Longitude", y = "Number of listings") +
  theme(plot.title=element_text(vjust=2, face = "bold"), 
        axis.title.x=element_text(vjust=-1, face = "bold"),
        axis.title.y=element_text(vjust=4, face = "bold"))

Not really. The x-centroid of mainland LA County should be around -118.2, so while the bulk of listings are skewed slightly to the left of that, I don’t suppose we could identify a gap here.


3. Number and Type of Listings < $1k

I’d like to look at the distribution of prices per room type a little more. But first I’m going to check on potential outliers again…

airbnb_data <- airbnb_data %>% arrange(desc(price))
head(airbnb_data)
# A tibble: 6 x 16
      id name  host_id host_name neighbourhood_g~ neighbourhood latitude longitude room_type price minimum_nights
   <int> <chr>   <int> <chr>     <chr>            <chr>            <dbl>     <dbl> <chr>     <int>          <int>
1 1.92e7 Hist~  1.34e8 Lorenzo   <NA>             Hollywood Hi~     34.1     -118. Entire h~ 25000              2
2 2.18e7 Beve~  1.59e8 Daniel    <NA>             Beverly Crest     34.1     -118. Private ~ 15000             20
3 2.95e6 Mali~  5.76e6 Yun       <NA>             Malibu            34.0     -119. Entire h~ 10000              1
4 3.07e6 Mali~  5.76e6 Yun       <NA>             Unincorporat~     34.1     -119. Entire h~ 10000              1
5 4.03e6 UNBE~  1.02e7 Robert    <NA>             Beverly Crest     34.1     -118. Entire h~ 10000              3
6 1.14e7 Cape~  4.82e7 Mary      <NA>             Malibu            34.0     -119. Entire h~ 10000             30
# ... with 5 more variables: number_of_reviews <int>, last_review <date>, reviews_per_month <dbl>,
#   calculated_host_listings_count <int>, availability_365 <int>

I tried excluding the $25,000 listing and setting the plotted limit to the next highest price ($15k), but that didn’t produce very interesting results either. I think I’d like to focus on the non-luxury listings for now, a.k.a. listings under the modest amount of $1,000 for one night’s humble stay.

price_1k <- airbnb_data %>% filter(price <= 1000)
ggplot(price_1k, aes(x = price, fill = room_type)) +
  geom_histogram(position = "dodge") +
  scale_fill_manual(values = c("#efa35c", "#4ab8b8", "#1b3764"), name = "Room Type") +
  labs(title = "Number and Type of Listings under 1,000 USD", x = "Price per night (USD)", y = "Number of listings") +
  theme(plot.title=element_text(vjust=2, face = "bold"), 
        axis.title.x=element_text(vjust=-1, face = "bold"),
        axis.title.y=element_text(vjust=4, face = "bold"))

The most affordable options, as we could’ve expected, are shared and private rooms.


4. Listing Price vs. Last Date Reviewed

There are some pretty crazy listing prices in LA - I’m curious if those actually get rented! Let’s take a look at the “last_review” variable

summary(airbnb_data$last_review)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max.         NA's 
"2010-03-28" "2018-06-07" "2018-09-02" "2018-05-13" "2018-09-23" "2018-10-05"       "9101" 

Most listings appear to have been rented in the last year, but there are some outliers going back to 2010 that I’m not particularly interested in. We’ll say good-bye to those.

I had trouble filtering through the “last_review” variable, so I reformatted it.. or something:

airbnb_data$last_review <-as.Date(airbnb_data$last_review,"%Y-%m-%d")
rented <- airbnb_data %>%
  filter(last_review >= "2013-01-01") %>%
  arrange(number_of_reviews)
summary(rented$last_review)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
"2013-01-02" "2018-06-07" "2018-09-02" "2018-05-13" "2018-09-23" "2018-10-05" 

That should also get rid of the NA values. Now we can look at the listings that have been rented at some point in the last 5 years:

ggplot(rented, aes(x = last_review, y = price, color = room_type, size = number_of_reviews)) +
  geom_jitter(shape = 1, stroke = 1.2, alpha = 0.4) +
  scale_color_manual(values = c("#efa35c", "#4ab8b8", "#1b3764"), guide = FALSE) +
  scale_size(breaks = c(1,10,50,100), labels = c(">=1",">=10",">=50",">=100"), range = c(2,12), name = "No. of Reviews") +
  facet_grid(. ~ room_type) +
  labs(title = "Listing Price vs. Last Date Reviewed", x = "Last Review", y = "Price/night (USD)") +
  theme_minimal() +
  theme(plot.title=element_text(vjust=3, face = "bold"), 
        axis.title.x=element_text(vjust=-1, face = "bold"),
        axis.title.y=element_text(vjust=4, face = "bold"),
        strip.text.x = element_text(size = 10, vjust = 2),
        strip.background = element_rect(color = FALSE))

So, $10,000 private rooms aren’t rented so often, but people really do pay that for entire homes and apartments! Listings in the in the $1,0000-5,000 range, however, aren’t as consistently rented.


5. Most Reviewed Neighbourhoods - the Size Instinct

In which neighbourhoods are listings more likely to be rented? I want to see which neighbourhood’s listings are most popular, in some sense. There’s no data for the number of times each listing has been rented, but we can look at the number of reviews.

To combat the Size Instinct, Rosling encourages us to “get things in proportion.” So let’s see if the neighbourhoods with the highest total number of reviews are also the neighbourhoods that get the most reviews per month.

We’ll revisit our nbhood_mean dataset and focus on the columns for total number of reviews (which we summed by room type and neighbourhood) and average number of reviews per month (averaged by the same variables).

First, I need a dataset of the 5 neighbourhoods, in each room type, with the highest total reviews. I’m sure there is a more efficient way to do this than to create 3 separate tables that we then bind together, but here we are for now. I’ll do the same for reviews per month.

reviews_total_entire <- nbhood_mean %>%
  filter(room_type == "Entire home/apt") %>%
  arrange(desc(number_of_reviews)) %>%
  slice(1:5)
reviews_total_private <- nbhood_mean %>%
  filter(room_type == "Private room") %>%
  arrange(desc(number_of_reviews)) %>%
  slice(1:5)
reviews_total_shared <- nbhood_mean %>%
  filter(room_type == "Shared room") %>%
  arrange(desc(number_of_reviews)) %>%
  slice(1:5)
reviews_top5_total <- rbind(reviews_total_entire, reviews_total_private, reviews_total_shared)
rm(reviews_total_entire, reviews_total_private, reviews_total_shared)
reviews_monthly_entire <- nbhood_mean %>%
  filter(room_type == "Entire home/apt") %>%
  arrange(desc(reviews_per_month)) %>%
  slice(1:5)
reviews_monthly_private <- nbhood_mean %>%
  filter(room_type == "Private room") %>%
  arrange(desc(reviews_per_month)) %>%
  slice(1:5)
reviews_monthly_shared <- nbhood_mean %>%
  filter(room_type == "Shared room") %>%
  arrange(desc(reviews_per_month)) %>%
  slice(1:5)
reviews_top5_monthly <- rbind(reviews_monthly_entire, reviews_monthly_private, reviews_monthly_shared)
rm(reviews_monthly_entire, reviews_monthly_private, reviews_monthly_shared)
head(reviews_top5_total)
# A tibble: 6 x 9
  neighbourhood   room_type       latitude longitude number_of_reviews last_review reviews_per_month listings price
  <chr>           <chr>              <dbl>     <dbl>             <int> <date>                  <dbl>    <int> <dbl>
1 Venice          Entire home/apt     34.0     -118.            109153 2018-05-22               2.16     2168  254.
2 Hollywood       Entire home/apt     34.1     -118.             58221 2018-06-10               2.18     2001  181.
3 Downtown        Entire home/apt     34.0     -118.             36128 2018-06-14               2.31     1300  206.
4 Silver Lake     Entire home/apt     34.1     -118.             31760 2018-05-05               2.02      689  163.
5 Hollywood Hills Entire home/apt     34.1     -118.             29296 2018-05-10               1.86      857  273.
6 Venice          Private room        34.0     -118.             23085 2018-03-07               2.21      511  105.
head(reviews_top5_monthly)
# A tibble: 6 x 9
  neighbourhood              room_type     latitude longitude number_of_revie~ last_review reviews_per_mon~ listings price
  <chr>                      <chr>            <dbl>     <dbl>            <int> <date>                 <dbl>    <int> <dbl>
1 Artesia                    Entire home/~     33.9     -118.               71 2018-09-23              7.02        4  144.
2 Unincorporated Santa Susa~ Entire home/~     34.4     -119.               34 2018-09-29              6.62        2  142 
3 Paramount                  Entire home/~     33.9     -118.              324 2018-09-11              5.57        2  112.
4 Del Aire                   Entire home/~     33.9     -118.              784 2018-06-18              5.23       14  110.
5 Bellflower                 Entire home/~     33.9     -118.              184 2018-09-28              5.16        6  127.
6 Manchester Square          Private room      34.0     -118.               43 2018-10-02              7.44        5   63 

I’m a little concerned about the longer neighbourhood names, so I’ll make some adjustments before plotting. I want to sub in line breaks in place of spaces - but not all the spaces, and not all of the labels, so it’s going to be a very silly, manual process until I learn how to be better:

reviews_top5_monthly$neighbourhood <- sub("Unincorporated Santa Susana ", "Santa Susana\\\n", reviews_top5_monthly$neighbourhood)
reviews_top5_monthly$neighbourhood <- sub("Hills Estates", "Hills\\\nEstates", reviews_top5_monthly$neighbourhood)
reviews_top5_monthly$neighbourhood <- sub("Hills/Crenshaw", "Hills/\\\nCrenshaw", reviews_top5_monthly$neighbourhood)

And the two plots:

ggplot(reviews_top5_total, aes(x = neighbourhood, y = number_of_reviews, color = room_type)) +
  geom_point(size = 4) + 
  geom_segment(aes(x = neighbourhood, xend = neighbourhood, y = 0, yend = number_of_reviews)) +
  coord_flip() +
  facet_grid(room_type ~ ., scale = "free_y") +
  scale_color_manual(values = c("#efa35c", "#4ab8b8", "#1b3764"), guide = FALSE) +
  labs(title = "Most Reviewed Neighbourhoods v1", subtitle = "Based on total number of reviews", x = "", y = "Total Reviews") +
  theme_minimal() +
  theme(plot.title=element_text(vjust=2, face = "bold"),
        plot.subtitle=element_text(color = "gray60"),
        axis.title.x=element_text(vjust=-1, face = "bold"),
        axis.title.y=element_text(vjust=4, face = "bold"),
        strip.text.y = element_text(size = 10.5, face = "bold"))

ggplot(reviews_top5_monthly, aes(x = neighbourhood, y = reviews_per_month, color = room_type)) +
  geom_point(size = 4) + 
  geom_segment(aes(x = neighbourhood, xend = neighbourhood, y = 0, yend = reviews_per_month)) +
  coord_flip() +
  facet_grid(room_type ~ ., scale = "free_y") +
  scale_color_manual(values = c("#efa35c", "#4ab8b8", "#1b3764"), guide = FALSE) +
  labs(title = "Most Reviewed Neighbourhoods v2", subtitle = "Based on reviews per month (average of listings)", x = "", y = "Average Reviews per Month") +  theme_minimal() +
  theme(plot.title=element_text(vjust=2, face = "bold"),
        plot.subtitle=element_text(color = "gray60"),
        axis.title.x=element_text(vjust=-1, face = "bold"),
        axis.title.y=element_text(vjust=4, face = "bold"),
        strip.text.y = element_text(size = 10.5, face = "bold"))

The top neighbourhoods are totally different using the two different metrics! There are certainly more listings in Venice, Hollywood, & co., and thus more total reviews, but when we look at the review rate in version 2, those neighbourhoods are reviewed much more frequently. Version 1 also suggests that entire home/apt options get way more reviews than private and shared rooms, but room type doesn’t appear to make much of a difference when we look at reviews per month.

LS0tDQp0aXRsZTogIlIgQXNzaWdubWVudCAyOiBBaXJCbkIgaW4gTEEiDQpzdWJ0aXRsZTogImJ5IE1vbmljYSEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUbyBzdGFydDogaW1wb3J0IHRoZSB0cnVzdHkgdGlkeXZlcnNlIHBhY2thZ2UgYW5kIHRoZSBBaXJCbkIgZGF0YS4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dtYXApDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCg0Kc2V0d2QoIkM6L1VzZXJzL21vbmljYS9Eb2N1bWVudHMvQ291cnNlcy9RN19GYWxsMjAxOC9HRU9HIDIwOC93ZWVrNF9wcm9qZWN0IikNCnVudGlkeSA8LSByZWFkX2NzdigibGlzdGluZ3MuY3N2IikNCmhlYWQodW50aWR5KQ0Kc2FwcGx5KHVudGlkeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4KSkpDQpgYGANCg0KVGhpcyBkb2Vzbid0IGFjdHVhbGx5IGxvb2sgdG9vIG1lc3N5ISBJJ20gZ29pbmcgdG8gbG9vayBhdCB0aGUgcHJpY2UgdmFyaWFibGUgYSBsaXR0bGUgbW9yZSBjbG9zZWx5LCBzaW5jZSBJIHRoaW5rIEknbGwgYmUgdXNpbmcgaXQgYSBsb3QuDQoNCmBgYHtyfQ0Kc3VtbWFyeSh1bnRpZHkkcHJpY2UpDQpgYGANCg0KVGhhdCdzIHF1aXRlIGEgcmFuZ2Ugb2YgdmFsdWVzISBUaGUgMjUsMDAwIHNlZW1zIGxpa2UgYSByZWFsIG91dGxpZXIsIGFuZCBJIGRvbid0IHJlYWxseSB3YW50IHRvIGxvb2sgYXQgbGlzdGluZ3Mgd2l0aCBhIHByaWNlIG9mIDAgVVNELiANCg0KYGBge3J9DQphaXJibmJfZGF0YSA8LSB1bnRpZHkgJT4lIGZpbHRlcihwcmljZSA+PSAxMCkNCnN1bW1hcnkoYWlyYm5iX2RhdGEkcHJpY2UpDQpgYGANCg0KPGhyPg0KPGI+MS4gTGlzdGluZ3MgYnkgTmVpZ2hib3Job29kPC9iPg0KDQpJIHdhbnQgdG8gc3RhcnQgb2ZmIGJ5IG1hcHBpbmcgdGhlIHByaWNlIGFuZCBudW1iZXIgb2YgbGlzdGluZ3MgYnkgbmVpZ2hib3Job29kLCBmYWNldGVkIGJ5IHJvb20gdHlwZS4gV2UnbGwgbmVlZCBhIG5ldyBkYXRhc2V0IHRoYXQgaW5jbHVkZXMgdGhlIGF2ZXJhZ2UgcHJpY2UgYW5kIG51bWJlciBvZiBsaXN0aW5ncyBvZiBlYWNoIHJvb20gdHlwZSwgYXMgd2VsbCBhcyBwcm9kdWNlIGEgbmV3IHNldCBvZiBjb29yZGluYXRlcyBmb3IgZWFjaCBuZWlnaGJvcmhvb2QuIFRoZSBxdWlja2VzdCBhbmQgbm90LXNvLWFjY3VyYXRlIHdheSB0aGF0IEkgYW0gZ29pbmcgdG8gZG8gdGhpcyBpcyBieSBzaW1wbHkgYXZlcmFnaW5nIHRoZSBsYXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMgb2YgdGhlIGxpc3RpbmdzIGluIGVhY2ggbmVpZ2hib3Job29kLiBMYXN0bHksIEknbGwgYXJyYW5nZSB0aGUgdGFibGUgc28gdGhhdCB0aGUgaGlnaGVyIHZhbHVlcyB3aWxsIGJlIGRyYXduIGxhc3QuIA0KDQpgYGB7cn0NCm5iaG9vZF9tZWFuIDwtIGFpcmJuYl9kYXRhICU+JSANCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZCwgcm9vbV90eXBlKSAlPiUgDQogIHN1bW1hcmlzZSgNCiAgICBsYXRpdHVkZSA9IG1lYW4obGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksDQogICAgbG9uZ2l0dWRlID0gbWVhbihsb25naXR1ZGUsIG5hLnJtID0gVFJVRSksDQogICAgbnVtYmVyX29mX3Jldmlld3MgPSBzdW0obnVtYmVyX29mX3Jldmlld3MsIG5hLnJtID0gVFJVRSksDQogICAgbGFzdF9yZXZpZXcgPSBtZWFuKGxhc3RfcmV2aWV3LCBuYS5ybSA9IFRSVUUpLA0KICAgIHJldmlld3NfcGVyX21vbnRoID0gbWVhbihyZXZpZXdzX3Blcl9tb250aCwgbmEucm0gPSBUUlVFKSwNCiAgICBsaXN0aW5ncyA9IG4oKSwNCiAgICBwcmljZSA9IG1lYW4ocHJpY2UsIG5hLnJtID0gVFJVRSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGFycmFuZ2UocHJpY2UpDQoNCmhlYWQobmJob29kX21lYW4pDQpgYGANCg0KTmV4dCB3ZSdsbCBzZXQgdGhlIGV4dGVudCBvZiB0aGUgbWFwLi4uYnkgbGliZXJhbGx5IGJvcnJvd2luZyB0aGlzIHNuaXBwZXQgZnJvbSByLWJsb2dnZXJzLmNvbToNCg0KYGBge3J9DQpMQWhlaWdodCA8LSBtYXgoYWlyYm5iX2RhdGEkbGF0aXR1ZGUpIC0gbWluKGFpcmJuYl9kYXRhJGxhdGl0dWRlKQ0KTEF3aWR0aCA8LSBtYXgoYWlyYm5iX2RhdGEkbG9uZ2l0dWRlKSAtIG1pbihhaXJibmJfZGF0YSRsb25naXR1ZGUpDQpMQV9ib3JkZXJzIDwtIGMoYm90dG9tICA9IG1pbihhaXJibmJfZGF0YSRsYXRpdHVkZSkgIC0gMC4wNSAqIExBaGVpZ2h0LCANCiAgICAgICAgICAgICAgICAgdG9wICAgICA9IG1heChhaXJibmJfZGF0YSRsYXRpdHVkZSkgKyAwLjA1ICogTEFoZWlnaHQsDQogICAgICAgICAgICAgICAgIGxlZnQgICAgPSBtaW4oYWlyYm5iX2RhdGEkbG9uZ2l0dWRlKSAtIDAuMDUgKiBMQXdpZHRoLA0KICAgICAgICAgICAgICAgICByaWdodCAgID0gbWF4KGFpcmJuYl9kYXRhJGxvbmdpdHVkZSkgKyAwLjA1ICogTEF3aWR0aCkNCmBgYA0KDQpJIGRvbid0IHdhbnQgYW55IGxhYmVscyBjbHV0dGVyaW5nIHVwIHRoZSBiYWNrZ3JvdW5kLCBzbyBJJ20gdXNpbmcgdGhlICJ0b25lci1iYWNrZ3JvdW5kIiBTdGFtZW4gTWFwOg0KDQpgYGB7cn0NCkxBX21hcCA8LSBnZXRfc3RhbWVubWFwKExBX2JvcmRlcnMsIHpvb20gPSA4LCBtYXB0eXBlID0gInRvbmVyLWJhY2tncm91bmQiKQ0KYGBgDQoNCkFuZCBub3cgdGhlIG1hcHMhIEkgZG9uJ3QgdGhpbmsgaXQncyBuZWNlc3NhcnkgdG8gc2hvdyB0aGUgeC0veS1heGlzIGxhYmVscyBvciB0aXRsZXMsIHNvIEknbGwgcmVtb3ZlIHRob3NlLiANCg0KDQpgYGB7cn0NCmdnbWFwKExBX21hcCkgKw0KICBnZW9tX3BvaW50KG5iaG9vZF9tZWFuLCBtYXBwaW5nID0gYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSwgY29sb3IgPSBwcmljZSwgc2l6ZSA9IGxpc3RpbmdzKSkgKw0KICBzY2FsZV9jb2xvcl9kaXN0aWxsZXIocGFsZXR0ZSA9ICJSZFlsQnUiLCBkaXJlY3Rpb24gPSAtMSwgdHJhbnMgPSAibG9nMTAiLCBuYW1lID0gIlByaWNlIChVU0QpIikgKw0KICBzY2FsZV9zaXplKGJyZWFrcyA9IGMoMCwxLDEwLDEwMCwxMDAwKSwgbGFiZWxzID0gYygiPjAiLCI+PTEiLCI+PTEwIiwiPj0xMDAiLCI+PTEwMDAiKSwgcmFuZ2UgPSBjKDEsOSksIG5hbWUgPSAiTm8uIG9mIExpc3RpbmdzIikgKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSkgKw0KICBmYWNldF93cmFwKC4gfiByb29tX3R5cGUpICsNCiAgbGFicyh0aXRsZSA9ICJMaXN0aW5ncyBieSBOZWlnaGJvcmhvb2QiLCB4ID0gIiIsIHkgPSAiIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dCh2anVzdCA9IDQsIGZhY2UgPSAiYm9sZCIpLCANCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41LCB2anVzdCA9IDMsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBGQUxTRSkpDQpgYGANCg0KQXMgd2UgbWlnaHQgaGF2ZSBleHBlY3RlZCwgdGhlIG1vc3QgYWZmb3JkYWJsZSBvcHRpb25zIGFyZSBzaGFyZWQgcm9vbXMgaW4gRG93bnRvd24vQ2VudHJhbCBMQS4gVGhlIHByaWNpZXN0IGxpc3RpbmdzIGFyZSBvbiB0aGUgV2VzdHNpZGUgYW5kIGluIHRoZSBIb2xseXdvb2QgSGlsbHMsIHdoZXJlIHRoZXJlIGFyZSBmZXdlciBzaGFyZWQgcm9vbSBvcHRpb25zLiANCg0KPGhyPg0KPGI+Mi4gRWFzdC1XZXN0IERpc3RyaWJ1dGlvbiBvZiBMaXN0aW5ncy4uLmFrYSwgdGhlIEdhcCBJbnN0aW5jdD88L2I+DQoNClRoZXJlIHNlZW0gdG8gYmUgc28gbWFueSBsaXN0aW5ncyBvbiB0aGUgV2VzdHNpZGUuLi5hbmQgbW9yZSBleHBlbnNpdmUgb25lcyBhdCB0aGF0LiBJcyB0aGVyZSBhbiB1bmV2ZW4gZGlzdHJpYnV0aW9uIG9mIGxpc3RpbmdzIGluIExBLCBmcm9tIEVhc3QgdG8gV2VzdD8gSXMgdGhlcmUuLi5hIEdBUD8/Pw0KDQpgYGB7cn0NCmdncGxvdChhaXJibmJfZGF0YSwgYWVzKHggPSBsb25naXR1ZGUsIGZpbGwgPSByb29tX3R5cGUpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA0MCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjZWZhMzVjIiwgIiM0YWI4YjgiLCAiIzFiMzc2NCIpLCBuYW1lID0gIlJvb20gVHlwZSIpICsNCiAgbGFicyh0aXRsZSA9ICJFYXN0LVdlc3QgRGlzdHJpYnV0aW9uIG9mIExpc3RpbmdzIiwgeCA9ICJMb25naXR1ZGUiLCB5ID0gIk51bWJlciBvZiBsaXN0aW5ncyIpICsNCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQodmp1c3Q9MiwgZmFjZSA9ICJib2xkIiksIA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KHZqdXN0PS0xLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfdGV4dCh2anVzdD00LCBmYWNlID0gImJvbGQiKSkNCmBgYA0KDQpOb3QgcmVhbGx5LiBUaGUgeC1jZW50cm9pZCBvZiBtYWlubGFuZCBMQSBDb3VudHkgc2hvdWxkIGJlIGFyb3VuZCAtMTE4LjIsIHNvIHdoaWxlIHRoZSBidWxrIG9mIGxpc3RpbmdzIGFyZSBza2V3ZWQgc2xpZ2h0bHkgdG8gdGhlIGxlZnQgb2YgdGhhdCwgSSBkb24ndCBzdXBwb3NlIHdlIGNvdWxkIGlkZW50aWZ5IGEgZ2FwIGhlcmUuDQoNCjxocj4NCjxiPjMuIE51bWJlciBhbmQgVHlwZSBvZiBMaXN0aW5ncyA8ICQxazwvYj4NCg0KSSdkIGxpa2UgdG8gbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHByaWNlcyBwZXIgcm9vbSB0eXBlIGEgbGl0dGxlIG1vcmUuIEJ1dCBmaXJzdCBJJ20gZ29pbmcgdG8gY2hlY2sgb24gcG90ZW50aWFsIG91dGxpZXJzIGFnYWluLi4uDQoNCmBgYHtyfQ0KYWlyYm5iX2RhdGEgPC0gYWlyYm5iX2RhdGEgJT4lIGFycmFuZ2UoZGVzYyhwcmljZSkpDQpoZWFkKGFpcmJuYl9kYXRhKQ0KYGBgDQoNCkkgdHJpZWQgZXhjbHVkaW5nIHRoZSAkMjUsMDAwIGxpc3RpbmcgYW5kIHNldHRpbmcgdGhlIHBsb3R0ZWQgbGltaXQgdG8gdGhlIG5leHQgaGlnaGVzdCBwcmljZSAoJDE1ayksIGJ1dCB0aGF0IGRpZG4ndCBwcm9kdWNlIHZlcnkgaW50ZXJlc3RpbmcgcmVzdWx0cyBlaXRoZXIuIEkgdGhpbmsgSSdkIGxpa2UgdG8gZm9jdXMgb24gdGhlIG5vbi1sdXh1cnkgbGlzdGluZ3MgZm9yIG5vdywgYS5rLmEuIGxpc3RpbmdzIHVuZGVyIHRoZSBtb2Rlc3QgYW1vdW50IG9mICQxLDAwMCBmb3Igb25lIG5pZ2h0J3MgaHVtYmxlIHN0YXkuDQoNCmBgYHtyfQ0KcHJpY2VfMWsgPC0gYWlyYm5iX2RhdGEgJT4lIGZpbHRlcihwcmljZSA8PSAxMDAwKQ0KDQpnZ3Bsb3QocHJpY2VfMWssIGFlcyh4ID0gcHJpY2UsIGZpbGwgPSByb29tX3R5cGUpKSArDQogIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjZWZhMzVjIiwgIiM0YWI4YjgiLCAiIzFiMzc2NCIpLCBuYW1lID0gIlJvb20gVHlwZSIpICsNCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgYW5kIFR5cGUgb2YgTGlzdGluZ3MgdW5kZXIgMSwwMDAgVVNEIiwgeCA9ICJQcmljZSBwZXIgbmlnaHQgKFVTRCkiLCB5ID0gIk51bWJlciBvZiBsaXN0aW5ncyIpICsNCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQodmp1c3Q9MiwgZmFjZSA9ICJib2xkIiksIA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KHZqdXN0PS0xLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfdGV4dCh2anVzdD00LCBmYWNlID0gImJvbGQiKSkNCmBgYA0KDQpUaGUgbW9zdCBhZmZvcmRhYmxlIG9wdGlvbnMsIGFzIHdlIGNvdWxkJ3ZlIGV4cGVjdGVkLCBhcmUgc2hhcmVkIGFuZCBwcml2YXRlIHJvb21zLg0KDQo8aHI+DQo8Yj40LiBMaXN0aW5nIFByaWNlIHZzLiBMYXN0IERhdGUgUmV2aWV3ZWQ8L2I+DQoNClRoZXJlIGFyZSBzb21lIHByZXR0eSBjcmF6eSBsaXN0aW5nIHByaWNlcyBpbiBMQSAtIEknbSBjdXJpb3VzIGlmIHRob3NlIGFjdHVhbGx5IGdldCByZW50ZWQhIExldCdzIHRha2UgYSBsb29rIGF0IHRoZSAibGFzdF9yZXZpZXciIHZhcmlhYmxlDQoNCmBgYHtyfQ0Kc3VtbWFyeShhaXJibmJfZGF0YSRsYXN0X3JldmlldykNCmBgYA0KDQpNb3N0IGxpc3RpbmdzIGFwcGVhciB0byBoYXZlIGJlZW4gcmVudGVkIGluIHRoZSBsYXN0IHllYXIsIGJ1dCB0aGVyZSBhcmUgc29tZSBvdXRsaWVycyBnb2luZyBiYWNrIHRvIDIwMTAgdGhhdCBJJ20gbm90IHBhcnRpY3VsYXJseSBpbnRlcmVzdGVkIGluLiBXZSdsbCBzYXkgZ29vZC1ieWUgdG8gdGhvc2UuDQoNCkkgaGFkIHRyb3VibGUgZmlsdGVyaW5nIHRocm91Z2ggdGhlICJsYXN0X3JldmlldyIgdmFyaWFibGUsIHNvIEkgcmVmb3JtYXR0ZWQgaXQuLiBvciBzb21ldGhpbmc6DQoNCmBgYHtyfQ0KYWlyYm5iX2RhdGEkbGFzdF9yZXZpZXcgPC1hcy5EYXRlKGFpcmJuYl9kYXRhJGxhc3RfcmV2aWV3LCIlWS0lbS0lZCIpDQoNCnJlbnRlZCA8LSBhaXJibmJfZGF0YSAlPiUNCiAgZmlsdGVyKGxhc3RfcmV2aWV3ID49ICIyMDEzLTAxLTAxIikgJT4lDQogIGFycmFuZ2UobnVtYmVyX29mX3Jldmlld3MpDQoNCnN1bW1hcnkocmVudGVkJGxhc3RfcmV2aWV3KQ0KYGBgDQoNClRoYXQgc2hvdWxkIGFsc28gZ2V0IHJpZCBvZiB0aGUgTkEgdmFsdWVzLiBOb3cgd2UgY2FuIGxvb2sgYXQgdGhlIGxpc3RpbmdzIHRoYXQgaGF2ZSBiZWVuIHJlbnRlZCBhdCBzb21lIHBvaW50IGluIHRoZSBsYXN0IDUgeWVhcnM6DQoNCmBgYHtyfQ0KZ2dwbG90KHJlbnRlZCwgYWVzKHggPSBsYXN0X3JldmlldywgeSA9IHByaWNlLCBjb2xvciA9IHJvb21fdHlwZSwgc2l6ZSA9IG51bWJlcl9vZl9yZXZpZXdzKSkgKw0KICBnZW9tX2ppdHRlcihzaGFwZSA9IDEsIHN0cm9rZSA9IDEuMiwgYWxwaGEgPSAwLjQpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNlZmEzNWMiLCAiIzRhYjhiOCIsICIjMWIzNzY0IiksIGd1aWRlID0gRkFMU0UpICsNCiAgc2NhbGVfc2l6ZShicmVha3MgPSBjKDEsMTAsNTAsMTAwKSwgbGFiZWxzID0gYygiPj0xIiwiPj0xMCIsIj49NTAiLCI+PTEwMCIpLCByYW5nZSA9IGMoMiwxMiksIG5hbWUgPSAiTm8uIG9mIFJldmlld3MiKSArDQogIGZhY2V0X2dyaWQoLiB+IHJvb21fdHlwZSkgKw0KICBsYWJzKHRpdGxlID0gIkxpc3RpbmcgUHJpY2UgdnMuIExhc3QgRGF0ZSBSZXZpZXdlZCIsIHggPSAiTGFzdCBSZXZpZXciLCB5ID0gIlByaWNlL25pZ2h0IChVU0QpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dCh2anVzdD0zLCBmYWNlID0gImJvbGQiKSwgDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X3RleHQodmp1c3Q9LTEsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KHZqdXN0PTQsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCB2anVzdCA9IDIpLA0KICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gRkFMU0UpKQ0KYGBgDQoNClNvLCAkMTAsMDAwIHByaXZhdGUgcm9vbXMgYXJlbid0IHJlbnRlZCBzbyBvZnRlbiwgYnV0IHBlb3BsZSByZWFsbHkgZG8gcGF5IHRoYXQgZm9yIGVudGlyZSBob21lcyBhbmQgYXBhcnRtZW50cyEgTGlzdGluZ3MgaW4gdGhlIGluIHRoZSAkMSwwMDAwLTUsMDAwIHJhbmdlLCBob3dldmVyLCBhcmVuJ3QgYXMgY29uc2lzdGVudGx5IHJlbnRlZC4NCg0KPGhyPg0KPGI+NS4gTW9zdCBSZXZpZXdlZCBOZWlnaGJvdXJob29kcyAtIHRoZSBTaXplIEluc3RpbmN0PC9iPg0KDQpJbiB3aGljaCBuZWlnaGJvdXJob29kcyBhcmUgbGlzdGluZ3MgbW9yZSBsaWtlbHkgdG8gYmUgcmVudGVkPyBJIHdhbnQgdG8gc2VlIHdoaWNoIG5laWdoYm91cmhvb2QncyBsaXN0aW5ncyBhcmUgbW9zdCBwb3B1bGFyLCBpbiBzb21lIHNlbnNlLiBUaGVyZSdzIG5vIGRhdGEgZm9yIHRoZSBudW1iZXIgb2YgdGltZXMgZWFjaCBsaXN0aW5nIGhhcyBiZWVuIHJlbnRlZCwgYnV0IHdlIGNhbiBsb29rIGF0IHRoZSBudW1iZXIgb2YgcmV2aWV3cy4NCg0KVG8gY29tYmF0IHRoZSBTaXplIEluc3RpbmN0LCBSb3NsaW5nIGVuY291cmFnZXMgdXMgdG8gImdldCB0aGluZ3MgaW4gcHJvcG9ydGlvbi4iIFNvIGxldCdzIHNlZSBpZiB0aGUgbmVpZ2hib3VyaG9vZHMgd2l0aCB0aGUgaGlnaGVzdCB0b3RhbCBudW1iZXIgb2YgcmV2aWV3cyBhcmUgYWxzbyB0aGUgbmVpZ2hib3VyaG9vZHMgdGhhdCBnZXQgdGhlIG1vc3QgcmV2aWV3cyBwZXIgbW9udGguIA0KDQpXZSdsbCByZXZpc2l0IG91ciBuYmhvb2RfbWVhbiBkYXRhc2V0IGFuZCBmb2N1cyBvbiB0aGUgY29sdW1ucyBmb3IgdG90YWwgbnVtYmVyIG9mIHJldmlld3MgKHdoaWNoIHdlIHN1bW1lZCBieSByb29tIHR5cGUgYW5kIG5laWdoYm91cmhvb2QpIGFuZCBhdmVyYWdlIG51bWJlciBvZiByZXZpZXdzIHBlciBtb250aCAoYXZlcmFnZWQgYnkgdGhlIHNhbWUgdmFyaWFibGVzKS4NCiAgDQpGaXJzdCwgSSBuZWVkIGEgZGF0YXNldCBvZiB0aGUgNSBuZWlnaGJvdXJob29kcywgaW4gZWFjaCByb29tIHR5cGUsIHdpdGggdGhlIGhpZ2hlc3QgdG90YWwgcmV2aWV3cy4gSSdtIHN1cmUgdGhlcmUgaXMgYSBtb3JlIGVmZmljaWVudCB3YXkgdG8gZG8gdGhpcyB0aGFuIHRvIGNyZWF0ZSAzIHNlcGFyYXRlIHRhYmxlcyB0aGF0IHdlIHRoZW4gYmluZCB0b2dldGhlciwgYnV0IGhlcmUgd2UgYXJlIGZvciBub3cuIEknbGwgZG8gdGhlIHNhbWUgZm9yIHJldmlld3MgcGVyIG1vbnRoLg0KDQpgYGB7cn0NCnJldmlld3NfdG90YWxfZW50aXJlIDwtIG5iaG9vZF9tZWFuICU+JQ0KICBmaWx0ZXIocm9vbV90eXBlID09ICJFbnRpcmUgaG9tZS9hcHQiKSAlPiUNCiAgYXJyYW5nZShkZXNjKG51bWJlcl9vZl9yZXZpZXdzKSkgJT4lDQogIHNsaWNlKDE6NSkNCnJldmlld3NfdG90YWxfcHJpdmF0ZSA8LSBuYmhvb2RfbWVhbiAlPiUNCiAgZmlsdGVyKHJvb21fdHlwZSA9PSAiUHJpdmF0ZSByb29tIikgJT4lDQogIGFycmFuZ2UoZGVzYyhudW1iZXJfb2ZfcmV2aWV3cykpICU+JQ0KICBzbGljZSgxOjUpDQpyZXZpZXdzX3RvdGFsX3NoYXJlZCA8LSBuYmhvb2RfbWVhbiAlPiUNCiAgZmlsdGVyKHJvb21fdHlwZSA9PSAiU2hhcmVkIHJvb20iKSAlPiUNCiAgYXJyYW5nZShkZXNjKG51bWJlcl9vZl9yZXZpZXdzKSkgJT4lDQogIHNsaWNlKDE6NSkNCnJldmlld3NfdG9wNV90b3RhbCA8LSByYmluZChyZXZpZXdzX3RvdGFsX2VudGlyZSwgcmV2aWV3c190b3RhbF9wcml2YXRlLCByZXZpZXdzX3RvdGFsX3NoYXJlZCkNCnJtKHJldmlld3NfdG90YWxfZW50aXJlLCByZXZpZXdzX3RvdGFsX3ByaXZhdGUsIHJldmlld3NfdG90YWxfc2hhcmVkKQ0KDQpyZXZpZXdzX21vbnRobHlfZW50aXJlIDwtIG5iaG9vZF9tZWFuICU+JQ0KICBmaWx0ZXIocm9vbV90eXBlID09ICJFbnRpcmUgaG9tZS9hcHQiKSAlPiUNCiAgYXJyYW5nZShkZXNjKHJldmlld3NfcGVyX21vbnRoKSkgJT4lDQogIHNsaWNlKDE6NSkNCnJldmlld3NfbW9udGhseV9wcml2YXRlIDwtIG5iaG9vZF9tZWFuICU+JQ0KICBmaWx0ZXIocm9vbV90eXBlID09ICJQcml2YXRlIHJvb20iKSAlPiUNCiAgYXJyYW5nZShkZXNjKHJldmlld3NfcGVyX21vbnRoKSkgJT4lDQogIHNsaWNlKDE6NSkNCnJldmlld3NfbW9udGhseV9zaGFyZWQgPC0gbmJob29kX21lYW4gJT4lDQogIGZpbHRlcihyb29tX3R5cGUgPT0gIlNoYXJlZCByb29tIikgJT4lDQogIGFycmFuZ2UoZGVzYyhyZXZpZXdzX3Blcl9tb250aCkpICU+JQ0KICBzbGljZSgxOjUpDQpyZXZpZXdzX3RvcDVfbW9udGhseSA8LSByYmluZChyZXZpZXdzX21vbnRobHlfZW50aXJlLCByZXZpZXdzX21vbnRobHlfcHJpdmF0ZSwgcmV2aWV3c19tb250aGx5X3NoYXJlZCkNCnJtKHJldmlld3NfbW9udGhseV9lbnRpcmUsIHJldmlld3NfbW9udGhseV9wcml2YXRlLCByZXZpZXdzX21vbnRobHlfc2hhcmVkKQ0KDQpoZWFkKHJldmlld3NfdG9wNV90b3RhbCkNCmhlYWQocmV2aWV3c190b3A1X21vbnRobHkpDQpgYGANCg0KSSdtIGEgbGl0dGxlIGNvbmNlcm5lZCBhYm91dCB0aGUgbG9uZ2VyIG5laWdoYm91cmhvb2QgbmFtZXMsIHNvIEknbGwgbWFrZSBzb21lIGFkanVzdG1lbnRzIGJlZm9yZSBwbG90dGluZy4gSSB3YW50IHRvIHN1YiBpbiBsaW5lIGJyZWFrcyBpbiBwbGFjZSBvZiBzcGFjZXMgLSBidXQgbm90ICphbGwqIHRoZSBzcGFjZXMsIGFuZCBub3QgKmFsbCogb2YgdGhlIGxhYmVscywgc28gaXQncyBnb2luZyB0byBiZSBhIHZlcnkgc2lsbHksIG1hbnVhbCBwcm9jZXNzIHVudGlsIEkgbGVhcm4gaG93IHRvIGJlIGJldHRlcjoNCg0KYGBge3J9DQpyZXZpZXdzX3RvcDVfbW9udGhseSRuZWlnaGJvdXJob29kIDwtIHN1YigiVW5pbmNvcnBvcmF0ZWQgU2FudGEgU3VzYW5hICIsICJTYW50YSBTdXNhbmFcXFxuIiwgcmV2aWV3c190b3A1X21vbnRobHkkbmVpZ2hib3VyaG9vZCkNCnJldmlld3NfdG9wNV9tb250aGx5JG5laWdoYm91cmhvb2QgPC0gc3ViKCJIaWxscyBFc3RhdGVzIiwgIkhpbGxzXFxcbkVzdGF0ZXMiLCByZXZpZXdzX3RvcDVfbW9udGhseSRuZWlnaGJvdXJob29kKQ0KcmV2aWV3c190b3A1X21vbnRobHkkbmVpZ2hib3VyaG9vZCA8LSBzdWIoIkhpbGxzL0NyZW5zaGF3IiwgIkhpbGxzL1xcXG5DcmVuc2hhdyIsIHJldmlld3NfdG9wNV9tb250aGx5JG5laWdoYm91cmhvb2QpDQpgYGANCg0KQW5kIHRoZSB0d28gcGxvdHM6IA0KDQpgYGB7cn0NCmdncGxvdChyZXZpZXdzX3RvcDVfdG90YWwsIGFlcyh4ID0gbmVpZ2hib3VyaG9vZCwgeSA9IG51bWJlcl9vZl9yZXZpZXdzLCBjb2xvciA9IHJvb21fdHlwZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKyANCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbmVpZ2hib3VyaG9vZCwgeGVuZCA9IG5laWdoYm91cmhvb2QsIHkgPSAwLCB5ZW5kID0gbnVtYmVyX29mX3Jldmlld3MpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGZhY2V0X2dyaWQocm9vbV90eXBlIH4gLiwgc2NhbGUgPSAiZnJlZV95IikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI2VmYTM1YyIsICIjNGFiOGI4IiwgIiMxYjM3NjQiKSwgZ3VpZGUgPSBGQUxTRSkgKw0KICBsYWJzKHRpdGxlID0gIk1vc3QgUmV2aWV3ZWQgTmVpZ2hib3VyaG9vZHMgdjEiLCBzdWJ0aXRsZSA9ICJCYXNlZCBvbiB0b3RhbCBudW1iZXIgb2YgcmV2aWV3cyIsIHggPSAiIiwgeSA9ICJUb3RhbCBSZXZpZXdzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dCh2anVzdD0yLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoY29sb3IgPSAiZ3JheTYwIiksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X3RleHQodmp1c3Q9LTEsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KHZqdXN0PTQsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUsIGZhY2UgPSAiYm9sZCIpKQ0KDQpnZ3Bsb3QocmV2aWV3c190b3A1X21vbnRobHksIGFlcyh4ID0gbmVpZ2hib3VyaG9vZCwgeSA9IHJldmlld3NfcGVyX21vbnRoLCBjb2xvciA9IHJvb21fdHlwZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKyANCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbmVpZ2hib3VyaG9vZCwgeGVuZCA9IG5laWdoYm91cmhvb2QsIHkgPSAwLCB5ZW5kID0gcmV2aWV3c19wZXJfbW9udGgpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGZhY2V0X2dyaWQocm9vbV90eXBlIH4gLiwgc2NhbGUgPSAiZnJlZV95IikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI2VmYTM1YyIsICIjNGFiOGI4IiwgIiMxYjM3NjQiKSwgZ3VpZGUgPSBGQUxTRSkgKw0KICBsYWJzKHRpdGxlID0gIk1vc3QgUmV2aWV3ZWQgTmVpZ2hib3VyaG9vZHMgdjIiLCBzdWJ0aXRsZSA9ICJCYXNlZCBvbiByZXZpZXdzIHBlciBtb250aCAoYXZlcmFnZSBvZiBsaXN0aW5ncykiLCB4ID0gIiIsIHkgPSAiQXZlcmFnZSBSZXZpZXdzIHBlciBNb250aCIpICsgIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHZqdXN0PTIsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChjb2xvciA9ICJncmF5NjAiKSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dCh2anVzdD0tMSwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQodmp1c3Q9NCwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAuNSwgZmFjZSA9ICJib2xkIikpDQpgYGANCg0KVGhlIHRvcCBuZWlnaGJvdXJob29kcyBhcmUgdG90YWxseSBkaWZmZXJlbnQgdXNpbmcgdGhlIHR3byBkaWZmZXJlbnQgbWV0cmljcyEgVGhlcmUgYXJlIGNlcnRhaW5seSAqbW9yZSogbGlzdGluZ3MgaW4gVmVuaWNlLCBIb2xseXdvb2QsICYgY28uLCBhbmQgdGh1cyBtb3JlIHRvdGFsIHJldmlld3MsIGJ1dCB3aGVuIHdlIGxvb2sgYXQgdGhlIHJldmlldyByYXRlIGluIHZlcnNpb24gMiwgdGhvc2UgbmVpZ2hib3VyaG9vZHMgYXJlIHJldmlld2VkIG11Y2ggbW9yZSBmcmVxdWVudGx5LiBWZXJzaW9uIDEgYWxzbyBzdWdnZXN0cyB0aGF0IGVudGlyZSBob21lL2FwdCBvcHRpb25zIGdldCB3YXkgbW9yZSByZXZpZXdzIHRoYW4gcHJpdmF0ZSBhbmQgc2hhcmVkIHJvb21zLCBidXQgcm9vbSB0eXBlIGRvZXNuJ3QgYXBwZWFyIHRvIG1ha2UgbXVjaCBvZiBhIGRpZmZlcmVuY2Ugd2hlbiB3ZSBsb29rIGF0IHJldmlld3MgcGVyIG1vbnRoLg==