Jeff Ellington
3/18/18

Q0. EDA & Data Transformations

The data set contains 2,928 observations of 8 variables related to MLS housing listings across 4 ZIP Codes. For the analysis, we will assume the data were not generated/collected via a random process. Prior to evaluating the data, I closely reviewed the data and didn’t see any obvious errors (NA’s, Infinite numbers, divergent trends, etc). In preparing the data for analysis, I made 10 transformations.

Data Transformations

1. day — Day of week (Factor with 7 levels)
2. month — Month of year (Factor with 12 levels)
3. qtr - Quarter of year (Factor with 4 levels)
  Note: Quarter is done evenly with 732 observaations in each quarter beginning 1/7/17, NOT purely by date
4. mls_rs_listing - Reduced mls_listings by 10% based on St. Louis FED data of New to Resale ratio (Integer)
5. pct_contracts_total - MLS Contracts as a percent of MLS Listings (Numeric)
6. pct_contracts_rs - MLS Contracts as a percent of estimated MLS Resale Listings (Numeric)
7. od_pct_contracts - OD Contracts as a percent of OD Listings (Numeric)
8. od_visits_listing - Number of total OD visits divided by number of OD Listings (Numeric)
9. od_contracts_visit - Number of OD contracts per number of OD home visits (Numeric)
10. od_mls_listing_ratio - Ratio of OD listings to MLS listings, expressed as decimal (Numeric)

Q1. Opendoor 2017 Resale Performance

Prompt

We’ve given you disguised data for the MLS (market) and Opendoor. The spreadsheet includes counts of active listings, resale contracts, and related data. How enthusiastic or worried are you about Opendoor’s resale performance in 2017?

Analysis

To evaluate Opendoor’s performance, we first need to establish how Opendoor’s core business model operates. Opendoor is a two-sided marketplace. It’s supply side allows consumers to sell their existing home for a ~6% fee without the hassle/delay of working with a traditional agent. Opendoor typically will make modest improvements (to boost home value) before listing the purchased home for resale. It’s demand side allows consumers/realtors to view Opendoor home listings in an app/browser and purchase homes they are interested in. For this question, we will ignore any of the potential business line extensions (financing, services, etc) and focus only on the marketplace dynamics.

We will evaluate Opendoor’s 2017 performance at both aggregate and cohort levels.

Aggregate Level

Listing Growth: Across all ZIP codes and price bands, Opendoor listings contracted 45% from 430 listings on 1/7/17 to 235 listings on 1/7/18. Over the same one year period, MLS listings contracted just 22% from 4,364 to 3,385. While this relative decline is unlikely a sign of overperformance, it’s not conclusively underperformance. The decline could also be attributable to a strategic decision.

Visitor Growth: Across all ZIP codes and price bands, Opendoor home visits dropped 25% from 2,556 visits on 1/7/17 to 1905 listings on 1/7/18. Over the same period, average visitors/listing increased from 5.9 to 8.1 (37%). Part of this was likely due to a reduction in OD housing listings, concentrating potential buyers in fewer homes. However, it’s possible this indicates Opendoor is selecting more attractive offerings.

Contract Conversion: Across all ZIP codes and price bands, Opendoor’s median contract conversion rate is 2.94% versus the overall MLS rate of 3.22%. This 8% delta is fairly small, especially considering the 11.5x difference in sample size (OD contracts: 3,668 vs. MLS contracts: 42,364).

Cohort Level

Splitting the data by ZIP Code (4 levels), Price Level (2 levels), and Quarter (4 levels) reveals interesting trends. This split implies 32 distinct cohorts.

Listing Growth: Between 1/7/17 and 1/7/18, Opendoor experienced significant listing growth (~100%) in listings <$200K. This trend holds across all ZIP Codes. In contrast, overall MLS listing count decreased considerably (~45%) over the same period for listings <$200k (again, similar across all ZIP codes). Conversely, for listings $200K+, the opposite trend holds. Opendoor’s aggregate $200+ listing count plummeted 78% over the year. Meanwhile, MLS listings grew modestly for the first three quarters of the year, before dropping off in Q4. Overall, there was a 10% reduction in MLS listing volume from the start of the year to the end. Opendoor’s listings decline accelerated in Q4 too, implying this trend was likely market driven. Finally, the ratio of Opendoor listings to total MLS listings as a percent varied dramatically across the two price levels. In 2017, Opendoor experienced 4x relative growth in <$200K vs. 3x relative contraction in $200K+ (again, trend holds across all ZIP codes). By the end of the year, Opendoor comprised 20% of the <$250K market. The 5 graphs below help visualize these trends. The graphs do not appear to show significant seasonality, except for the dip around the holidays. All 4 listing charts show an additional dip around Thanksgiving that lasts through New Years, before turning sharply up again. This seems normal and reflects real world dynamics.

      ggp.un +
        geom_jitter(aes(color=zip_code,y=mls_listings))+
        labs(x = "Time", y = "Listing Count", title = "MLS Listing Count: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

      ggp.un +
        geom_jitter(aes(color=zip_code,y=od_listings))+
        labs(x = "Time", y = "Listing Count", title = "OD Listing Count: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

      ggp.ov +
        geom_jitter(aes(color=zip_code,y=mls_listings))+
        labs(x = "Time", y = "Listing Count", title = "MLS Listing Count: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

      ggp.ov +
        geom_jitter(aes(color=zip_code,y=od_listings))+
        labs(x = "Time", y = "Listing Count", title = "OD Listing Count: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

      ggp <- ggplot(data.frame(data), aes(date,100*od_mls_listing_ratio))
      ggp +
        geom_point(aes(color = price_band))+
        labs(x = "Time", y = "Percent", title = "OD:MLS Listing Ratio (All Zip Codes)")+
        theme(axis.text.x=element_blank())

Visitor Growth: Visitor growth per listing trends were consistent across all ZIP codes. At an aggregate level, visitors per listing grew 37% over the year. However, the trends varied substantially between the two price levels. Visitors per <$200K listing fell 25% over the course of the year. Meanwhile, for listings $200K+, visits per listing grew over 60% during the year (from 5.2 to 8.5). In aggregate, the total number of daily visits divided by the total number of daily listings were 9.6 and 6.4, respectively, for <$200K and $200K+. Over the course of the year, it took, in aggregate and on average, 187 visits to generate a contract for a <$200K listing and 298 visits to generate a contract for a $200K+ listing. The two graphs below help visualize these trends. Furthermore, comparing these graphs to the listing graphs above shows a high degree of (inverse) correlation between visitors and listings. Given there is likely a reasonably stable supply of potential home buyers in the area, it’s likely that as the number of homes decreases the number of visits increases as home buyers are either spread less thin or have less options to filter on. Conversely, with many offerings, the visits likely get spread across a larger base.

      ggp.un +
        geom_jitter(aes(color=zip_code,y=od_visits_listing))+
        labs(x = "Time", y = "Count", title = "Visits Per Listing: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

      ggp.ov +
        geom_jitter(aes(color=zip_code,y=od_visits_listing))+
        labs(x = "Time", y = "Count", title = "Visits Per Listing: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

Contract Conversion: Looking at aggregate performance across each of the 4 quarters, Opendoor’s median contract conversion rate performance was on par with the overall MLS market. Opendoor lagged by 1% point in Q1, exceed by 0.25% point in Q2, and was virtually identical in Q3 and Q4. Across all 32 cohorts, Opendoor outperformed MLS in contract conversion rate 19 times (out of 32 possible). On an annual basis, among the 8 cohorts (4 ZIPs * 2 Prices), Opendoor outperformed 4 times and underperformed 4 times. Opendoor was more likely to outperform in the <$200K price level than in the $200K+ one (3 of it’s 4 outperforms were in <$200K). Opendoor is decidely better than MLS average in ZIP Code A and decidely worse in ZIP Code B. Opendoor tends to outperofrm in ZIP C and D, except for Q1 in <$200K and in Q4 for $200K+, when data are too thin to properly assess.

The two graphs below show the difference in OD vs. MLS contract conversion rate. The black line is 0, so anything above shows Opendoor converting better than MLS. The left scale is percentage points.

      ## <$200K subsection
        ggp.cohorts + 
          geom_point(aes(y=100*A..200K), color = "#F8766D")+
            geom_line(aes(y=100*A..200K), color = "#F8766D")+
          geom_point(aes(y=100*B..200K), color = "#7CAE00")+
            geom_line(aes(y=100*B..200K), color = "#7CAE00")+
          geom_point(aes(y=100*C..200K), color = "#00CFB4")+
            geom_line(aes(y=100*C..200K), color = "#00CFB4")+
          geom_point(aes(y=100*D..200K), color = "#C77CFF")+
            geom_line(aes(y=100*D..200K), color = "#C77CFF")+
            geom_line(aes(y=0), color="Black")+
            theme(legend.position = "right")+
          labs(x = "Quarter", y = "Difference: Percentage Points", title = "Difference in OD:MLS Contract/Listing Win Rate: <$200K, By Quarter")

      ## $200K+ subsection
        ggp.cohorts + 
          geom_point(aes(y=100*A.200K.), color = "#F8766D")+
            geom_line(aes(y=100*A.200K.), color = "#F8766D")+
          geom_point(aes(y=100*B.200K.), color = "#7CAE00")+
            geom_line(aes(y=100*B.200K.), color = "#7CAE00")+
          geom_point(aes(y=100*C.200K.), color = "#00CFB4")+
            geom_line(aes(y=100*C.200K.), color = "#00CFB4")+
          geom_point(aes(y=100*D.200K.), color = "#C77CFF")+
            geom_line(aes(y=100*D.200K.), color = "#C77CFF")+
            geom_line(aes(y=0), color = "Black")+
          labs(x = "Quarter", y = "Difference: Percentage Points", title = "Difference in OD:MLS Contract/Listing Win Rate: $200K+, By Quarter")

Conclusion

At an aggregate level, it’s inconclusive if Opendoor is under or overperforming. Listings are down 45% and visits are down 25%, yet visits/listing are up 37% and average conversion rate difference is only marginally lower (8%). If forced to make a conclusion on this data alone, I’d lean toward underperformance.

Looking at the cohort level data provides greater clarity. Opendoor overperformed in the <$200K category, growing listings nearly 2x over the year while only letting visitors/listing slide 25%. While it’s contract conversion rate steadily decreased over the year (potentially due to increasing supply), it still maintained a higher median conversion rate than the overall MLS market rate. Opendoor’s performance in $200K+ underperformed slightly over the year. Listings shrunk 78% and median contract conversion rate only increased in ZIPs A and B. The median contract conversion rate for ZIPs C and D fell to 0% as homes virtually stopped selling (just 13 contracts between the 2 ZIPs in Q4.) Note: I purposefully used median for contract conversion rate for just this reason. Using average provides “better” results, but on such thin data, it feels misleading. The reality is on most days in Q4 Opendoor is not selling $200K+ homes in ZIPs C and D.

Q3. Further Exploration of Hypothesis #1

Prompt

Chose one of your top hypotheses from Question 2 and assume you’ve validated that it’s the driver of the performance trend. Propose a plan of action to either course correct (in the instance of underperformance) or drive continued growth (if you’ve identified positive performance).

Analysis

Hypothesis #1: Opendoor is bidding differently on <$200K and $200K+ Homes: (Restated from Q2) An intriguing data point is the ratio of $200K+ listings to <$200K listings. Opendoor begins 2017 with a ratio of 4:1 (4 $200K : 1 <$200K) while general MLS listings are at 2:1. During Q1, Opendoor accelerates to a peak of 8:1, before precipitously declining to a ratio of 1:2 at the end of the year (a factor of 16x)! In comparison, MLS listings grow from 2:1 at the beginning of the sample set to a peak of 4:1 at mid-year, before settling at 3.25:1. Again, given the sample sizes are highly skewed (11.5:1), it’s not surprising to see a larger swing in Opendoor’s range. However, such a reversal likely implies a trend worth examining. My hypothesis is that Opendoor sees an economic opportunity in homes <$200K. Homes <$200k comprise ~30% of the overall market (similar across all ZIPs), naturally implying the median home price is above $250K. Perhaps Opendoor has developed a strategy for increasing the value of homes <$250K more substantially than the median home. If this is the case, Opendoor may be focusing on the low end of the market where it sees a greater opportunity for gross profit.

      par(mfrow=c(1,2))
      ## Opendoor
      ratio_200K.od <- ov.data$od_listings/un.data$od_listings
      plot(ratio_200K.od,main = "OD List: Ratio $200K+/<$200K ", xlab = "Time", ylab = "Ratio", col = "dodgerblue2")
      ## MLS
      ratio_200K.mls <- ov.data$mls_listings/un.data$mls_listings
      plot(ratio_200K.mls,main = "MLS List: Ratio $200K+/<$200K ", xlab = "Time", ylab = "Ratio", col = "grey75")

Expanded Strategy: Gross Profit Arbitrage <$200K: At this point we’ve validated that there is a gross profit arbitrage at <$200K, which is why Opendoor is so focused on purchasing homes in this segment. Here is the strategy I’d use to operationalize this profit opportunity.

Step I: Define characteristics most predictive of housing price (hypothetical list below):

- Previous sale/purchase price
- Estimated surrounding home value/historical prices
- Sub-divided ZIP codes for more accurate K-Nearest Neighbor approach/zoning
- Housing details: # of beds/baths, special attributes (gym/office/garage type), Sq Ft, Year Built
- Access to high speed internet / multiple cable providers
- Proximity to amenities (grocery, restaurants, parks, nature)
- Commute/Walk score

Step II: Add human inspection by local operator (Photos to assess health of paint, roof, grass, landscape, etc.)

Step III: Understand highest ROI home improvements that can be deployed cheaply/quickly and create immediate return (hypothetical list below):

- Paint
- Counter tops 
- Zero maintenance landscaping
- Appliances and light fixtures

Step IV: Build model to predict price delta of current condition and Opendoor improved condition

Step V: Create aggressive mail/digital campaign customized to houses with highest potential pricing delta. Use local operational team to help distribute and build awareness. Create unsolicited offers if legally allowed in area.

Step VI: Operationalize all procedures for fixing houses.

Step VII: Create best practices around listing and open houses. A/B test with marketing team to boost conversion and try to create multiple bids on any listed house.

Appendix: R Code

## OPENDOOR CITY DATA PROJECT (3/18/19)

## rm(list=ls()) ## clear Workspace

## data prep
  ## load data
    setwd("/Users/jeffreyellington/Dropbox/Code/OpenDoor/")
    data <- read.csv(file = "CityOpsData.csv") ## transformations done in excel

  ## first glance at data
    dim(data) ## 18 columns --> added 10 columns in excel 
    str(data) ## data types loaded correctly
    summary(data) ## no obvious errors
    sum(is.na(data)) ## no N/A


## data cleaning
  ## check day / month / qtr
    table(table(data$date)) ## 366 days
    table(data$date) ## 01/07 appears in both 2017 and 2018
    table(data$month) ## ok
    table(data$qtr) ## ok
  
  ## check zip code
    table(data$zip_code) ## ok

    
## cohort analysis 
  ## create 2 cohorts (2 Price Levels)
    un.data <- subset(data, data$price_band == "<$200K")
    ov.data <- subset(data, data$price_band == "$200K+")
  
  ## create 4 cohorts (4 Zip Codes)
    a.data <- subset(data, data$zip_code == "A")
    b.data <- subset(data, data$zip_code == "B")
    c.data <- subset(data, data$zip_code == "C")
    d.data <- subset(data, data$zip_code == "D")
  
  ## create 8 cohorts (4 ZipCodes * 2 Price Levels)
    a.un.data <- subset(data, data$zip_code == "A" & data$price_band == "<$200K")
    a.ov.data <- subset(data, data$zip_code == "A" & data$price_band == "$200K+")
    b.un.data <- subset(data, data$zip_code == "B" & data$price_band == "<$200K")
    b.ov.data <- subset(data, data$zip_code == "B" & data$price_band == "$200K+")
    c.un.data <- subset(data, data$zip_code == "C" & data$price_band == "<$200K")
    c.ov.data <- subset(data, data$zip_code == "C" & data$price_band == "$200K+")
    d.un.data <- subset(data, data$zip_code == "D" & data$price_band == "<$200K")
    d.ov.data <- subset(data, data$zip_code == "D" & data$price_band == "$200K+")
  
    366*8 ## dimensions correct 
  
  ## Plot to see if any systematic data error
    par(mar=c(4,4,4,4))
    par(mfrow=c(2,2)) ## Plots for MLS Listings by Zipcode for <$200k
    plot(a.un.data$mls_listings)
    plot(b.un.data$mls_listings)
    plot(c.un.data$mls_listings)
    plot(d.un.data$mls_listings)
    par(mfrow=c(2,2)) ## Plots for OD Listings by Zipcode for <$200k
    plot(a.un.data$od_listings)
    plot(b.un.data$od_listings)
    plot(c.un.data$od_listings)
    plot(d.un.data$od_listings)
    par(mfrow=c(2,2)) ## Plots for MLS Listings by Zipcode for $200k+
    plot(a.ov.data$mls_listings)
    plot(b.ov.data$mls_listings)
    plot(c.ov.data$mls_listings)
    plot(d.ov.data$mls_listings)
    par(mfrow=c(2,2)) ## Plots for OD Listings by Zipcode for $200k+
    plot(a.ov.data$od_listings)
    plot(b.ov.data$od_listings)
    plot(c.ov.data$od_listings)
    plot(d.ov.data$od_listings)
    ## data appears highly correlated across zipcodes for MLS and OD data in each price bracket

    
    
  ## load ggplot2 for graphing
    ## install.packages("ggplot2")
    library(ggplot2)
  
  ## Aggregate stats
    ## 1 Year Listing Growth
    Day1.OD <- sum(data$od_listings[data$date == "2017/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day366.OD <- sum(data$od_listings[data$date == "2018/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day1.MLS <-sum(data$mls_listings[data$date == "2017/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day366.MLS <-sum(data$mls_listings[data$date == "2018/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day1.OD
    Day366.OD
    Day1.MLS
    Day366.MLS
    (Day366.OD - Day1.OD)/Day1.OD
    (Day366.MLS - Day1.MLS)/Day1.MLS
    ## 1 Year Visitor Growth
    Day1.OD.hv <- sum(data$od_home_visits[data$date == "2017/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day366.OD.hv <- sum(data$od_home_visits[data$date == "2018/01/07" & data$zip_code == c("A", "B", "C", "D")])
    Day1.OD.hv
    Day366.OD.hv
    (Day366.OD.hv - Day1.OD.hv)/Day1.OD.hv
    Day1.OD.hv/Day1.OD
    Day366.OD.hv/Day366.OD
    
  ## comparing listing growth by price band and zip code
    ## <$200K
      data.frame(un.data)
      ggp.un <- ggplot(data.frame(un.data), aes(date))
    ## plotting MLS,OD listings
      ggp.un +
        geom_jitter(aes(color=zip_code,y=mls_listings))+
        labs(x = "Time", y = "Listing Count", title = "MLS Listing Count: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())
      ggp.un +
        geom_jitter(aes(color=zip_code,y=od_listings))+
        labs(x = "Time", y = "Listing Count", title = "OD Listing Count: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())

    ## $200K+
      data.frame(ov.data)
      ggp.ov <- ggplot(data.frame(ov.data), aes(date))
    ## plotting MLS,OD listings
      ggp.ov +
        geom_jitter(aes(color=zip_code,y=mls_listings))+
        labs(x = "Time", y = "Listing Count", title = "MLS Listing Count: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())
      ggp.ov +
        geom_jitter(aes(color=zip_code,y=od_listings))+
        labs(x = "Time", y = "Listing Count", title = "OD Listing Count: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())
      
    ## $200K+::<$200K Ratio
      par(mfrow=c(1,2))
      ## Opendoor
      ratio_200K.od <- ov.data$od_listings/un.data$od_listings
      plot(ratio_200K.od,main = "OD Listings: Ratio of $200K+/<$200K ", xlab = "Time", ylab = "Ratio", col = "dodgerblue2")
      ## MLS
      ratio_200K.mls <- ov.data$mls_listings/un.data$mls_listings
      plot(ratio_200K.mls,main = "MLS Listings: Ratio of $200K+/<$200K ", xlab = "Time", ylab = "Ratio", col = "grey75")
        

  
  ## plotting OD to MLS listing ratio 
    ## full data
      data.frame(data)
      ggp <- ggplot(data.frame(data), aes(date,100*od_mls_listing_ratio))
      ggp +
        geom_point(aes(color = price_band))+
        labs(x = "Time", y = "Percent", title = "OD:MLS Listing Ratio (All Zip Codes)")+
        theme(axis.text.x=element_blank())
  
      
  ## comparing visit data by price band and zip code
    ## <$200K
      data.frame(un.data)
      ggp.un <- ggplot(data.frame(un.data), aes(date))
    ## plotting visits as a function of listings
      ggp.un +
        geom_jitter(aes(color=zip_code,y=od_visits_listing))+
        labs(x = "Time", y = "Count", title = "Visits Per Listing: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())
    ## plotting contracts as a function of visits
      ggp.un +
        geom_jitter(aes(color=zip_code,y=100*od_contracts_visit))+
        labs(x = "Time", y = "Percent", title = "Contracts Per Visit: <$200K (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank()) 

    ## $200K+
      data.frame(ov.data)
      ggp.ov <- ggplot(data.frame(ov.data), aes(date))
    ## plotting visits as a function of listings
      ggp.ov +
        geom_jitter(aes(color=zip_code,y=od_visits_listing))+
        labs(x = "Time", y = "Count", title = "Visits Per Listing: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank())
    ## plotting contracts as a function of visits
      ggp.ov +
        geom_jitter(aes(color=zip_code,y=100*od_contracts_visit))+
        labs(x = "Time", y = "Percent", title = "Contracts Per Visit: $200K+ (Zip Codes A,B,C,D)")+
        theme(axis.text.x=element_blank()) 

  ## evaluating contracts per listing by cohort
    ## OD average by cohort
      a.u.od <- median(a.un.data$od_contracts/a.un.data$od_listings)
      a.o.od <- median(a.ov.data$od_contracts/a.ov.data$od_listings)
      b.u.od <- median(b.un.data$od_contracts/b.un.data$od_listings)
      b.o.od <- median(b.ov.data$od_contracts/b.ov.data$od_listings)
      c.u.od <- median(c.un.data$od_contracts/c.un.data$od_listings)
      c.o.od <- median(c.ov.data$od_contracts/c.ov.data$od_listings)
      d.u.od <- median(d.un.data$od_contracts/d.un.data$od_listings)
      d.o.od <- median(d.ov.data$od_contracts/d.ov.data$od_listings)
    ## OD average by cohort
      a.u.mls <- median(a.un.data$mls_contracts/a.un.data$mls_listings)
      a.o.mls <- median(a.ov.data$mls_contracts/a.ov.data$mls_listings)
      b.u.mls <- median(b.un.data$mls_contracts/b.un.data$mls_listings)
      b.o.mls <- median(b.ov.data$mls_contracts/b.ov.data$mls_listings)
      c.u.mls <- median(c.un.data$mls_contracts/c.un.data$mls_listings)
      c.o.mls <- median(c.ov.data$mls_contracts/c.ov.data$mls_listings)
      d.u.mls <- median(d.un.data$mls_contracts/d.un.data$mls_listings)
      d.o.mls <- median(d.ov.data$mls_contracts/d.ov.data$mls_listings)
    ## create lists and dataframes
      od.medians <- 100*c(a.u.od, a.o.od, b.u.od, b.o.od, c.u.od, c.o.od, d.u.od, d.o.od)
      mls.medians <- 100*c(a.u.mls, a.o.mls, b.u.mls, b.o.mls, c.u.mls, c.o.mls, d.u.mls, d.o.mls)
      diff.medians <- (od.medians-mls.medians)
      cohort.names <- c("A <$200K", "A $200K+","B <$200K", "B $200K+","C <$200K", "C $200K+","D <$200K", "D $200K+")
      cohort.df <- data.frame(Cohort = cohort.names, ODmedian = od.medians, MLSmedian = mls.medians, Diff=diff.medians)
      cohort.df
    ## adding time series information by quarter
      qtr.data <- read.csv(file = "CityCohorts.csv") ## read in Pivot Table from excel, ignore error message
      qtr.data 
      ## 32 data points: 4 Quarters * 2 Price Levels * 4 Zip Codes
      ## QTR data is result of OD median contract win rate/MLS median contract win rate (so, it reflects the difference in % points)
      qtr.data$QTR <- c(1:4)
      qtr.data$QTR <- as.numeric(qtr.data$QTR)
      data.frame(qtr.data)
      str(qtr.data)
      ggp.cohorts <- ggplot(data.frame(qtr.data), aes(x=QTR))
      ## <$200K subsection
        ggp.cohorts + 
          geom_point(aes(y=100*A..200K), color = "#F8766D")+
            geom_line(aes(y=100*A..200K), color = "#F8766D")+
          geom_point(aes(y=100*B..200K), color = "#7CAE00")+
            geom_line(aes(y=100*B..200K), color = "#7CAE00")+
          geom_point(aes(y=100*C..200K), color = "#00CFB4")+
            geom_line(aes(y=100*C..200K), color = "#00CFB4")+
          geom_point(aes(y=100*D..200K), color = "#C77CFF")+
            geom_line(aes(y=100*D..200K), color = "#C77CFF")+
            geom_line(aes(y=0), color="Black")+
            theme(legend.position = "right")+
          labs(x = "Quarter", y = "Difference: Percentage Points", title = "Difference in OD:MLS Contract/Listing Win Rate: <$200K, By Quarter")
      ## $200K+ subsection
        ggp.cohorts + 
          geom_point(aes(y=100*A.200K.), color = "#F8766D")+
            geom_line(aes(y=100*A.200K.), color = "#F8766D")+
          geom_point(aes(y=100*B.200K.), color = "#7CAE00")+
            geom_line(aes(y=100*B.200K.), color = "#7CAE00")+
          geom_point(aes(y=100*C.200K.), color = "#00CFB4")+
            geom_line(aes(y=100*C.200K.), color = "#00CFB4")+
          geom_point(aes(y=100*D.200K.), color = "#C77CFF")+
            geom_line(aes(y=100*D.200K.), color = "#C77CFF")+
            geom_line(aes(y=0), color = "Black")+
          labs(x = "Quarter", y = "Difference: Percentage Points", title = "Difference in OD:MLS Contract/Listing Win Rate: $200K+, By Quarter")
LS0tCnRpdGxlOiAiT3BlbmRvb3I6IENpdHkgT3BzIERhdGEgUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIyMjIyBKZWZmIEVsbGluZ3RvbgojIyMjIyAzLzE4LzE4CgojIyMgUTAuICBFREEgJiBEYXRhIFRyYW5zZm9ybWF0aW9ucwpUaGUgZGF0YSBzZXQgY29udGFpbnMgMiw5Mjggb2JzZXJ2YXRpb25zIG9mIDggdmFyaWFibGVzIHJlbGF0ZWQgdG8gTUxTIGhvdXNpbmcgbGlzdGluZ3MgYWNyb3NzIDQgWklQIENvZGVzLiBGb3IgdGhlIGFuYWx5c2lzLCB3ZSB3aWxsIGFzc3VtZSB0aGUgZGF0YSB3ZXJlICoqbm90KiogZ2VuZXJhdGVkL2NvbGxlY3RlZCB2aWEgYSByYW5kb20gcHJvY2Vzcy4gUHJpb3IgdG8gZXZhbHVhdGluZyB0aGUgZGF0YSwgSSBjbG9zZWx5IHJldmlld2VkIHRoZSBkYXRhIGFuZCBkaWRuJ3Qgc2VlIGFueSBvYnZpb3VzIGVycm9ycyAoTkEncywgSW5maW5pdGUgbnVtYmVycywgZGl2ZXJnZW50IHRyZW5kcywgZXRjKS4gSW4gcHJlcGFyaW5nIHRoZSBkYXRhIGZvciBhbmFseXNpcywgSSBtYWRlIDEwIHRyYW5zZm9ybWF0aW9ucy4KCiMjIyMgRGF0YSBUcmFuc2Zvcm1hdGlvbnMKCiAgICAxLiBkYXkg4oCUIERheSBvZiB3ZWVrIChGYWN0b3Igd2l0aCA3IGxldmVscykKICAgIDIuIG1vbnRoIOKAlCBNb250aCBvZiB5ZWFyIChGYWN0b3Igd2l0aCAxMiBsZXZlbHMpCiAgICAzLiBxdHIgLSBRdWFydGVyIG9mIHllYXIgKEZhY3RvciB3aXRoIDQgbGV2ZWxzKQogICAgICBOb3RlOiBRdWFydGVyIGlzIGRvbmUgZXZlbmx5IHdpdGggNzMyIG9ic2VydmFhdGlvbnMgaW4gZWFjaCBxdWFydGVyIGJlZ2lubmluZyAxLzcvMTcsIE5PVCBwdXJlbHkgYnkgZGF0ZQogICAgNC4gbWxzX3JzX2xpc3RpbmcgLSBSZWR1Y2VkIG1sc19saXN0aW5ncyBieSAxMCUgYmFzZWQgb24gU3QuIExvdWlzIEZFRCBkYXRhIG9mIE5ldyB0byBSZXNhbGUgcmF0aW8gKEludGVnZXIpCiAgICA1LiBwY3RfY29udHJhY3RzX3RvdGFsIC0gTUxTIENvbnRyYWN0cyBhcyBhIHBlcmNlbnQgb2YgTUxTIExpc3RpbmdzIChOdW1lcmljKQogICAgNi4gcGN0X2NvbnRyYWN0c19ycyAtIE1MUyBDb250cmFjdHMgYXMgYSBwZXJjZW50IG9mIGVzdGltYXRlZCBNTFMgUmVzYWxlIExpc3RpbmdzIChOdW1lcmljKQogICAgNy4gb2RfcGN0X2NvbnRyYWN0cyAtIE9EIENvbnRyYWN0cyBhcyBhIHBlcmNlbnQgb2YgT0QgTGlzdGluZ3MgKE51bWVyaWMpCiAgICA4LiBvZF92aXNpdHNfbGlzdGluZyAtIE51bWJlciBvZiB0b3RhbCBPRCB2aXNpdHMgZGl2aWRlZCBieSBudW1iZXIgb2YgT0QgTGlzdGluZ3MgKE51bWVyaWMpCiAgICA5LiBvZF9jb250cmFjdHNfdmlzaXQgLSBOdW1iZXIgb2YgT0QgY29udHJhY3RzIHBlciBudW1iZXIgb2YgT0QgaG9tZSB2aXNpdHMgKE51bWVyaWMpCiAgICAxMC4gb2RfbWxzX2xpc3RpbmdfcmF0aW8gLSBSYXRpbyBvZiBPRCBsaXN0aW5ncyB0byBNTFMgbGlzdGluZ3MsIGV4cHJlc3NlZCBhcyBkZWNpbWFsIChOdW1lcmljKQoKIyMjIFExLiBPcGVuZG9vciAyMDE3IFJlc2FsZSBQZXJmb3JtYW5jZQojIyMjIFByb21wdApXZeKAmXZlIGdpdmVuIHlvdSBkaXNndWlzZWQgZGF0YSBmb3IgdGhlIE1MUyAobWFya2V0KSBhbmQgT3BlbmRvb3IuIFRoZSBzcHJlYWRzaGVldCBpbmNsdWRlcyBjb3VudHMgb2YgYWN0aXZlIGxpc3RpbmdzLCByZXNhbGUgY29udHJhY3RzLCBhbmQgcmVsYXRlZCBkYXRhLiBIb3cgZW50aHVzaWFzdGljIG9yIHdvcnJpZWQgYXJlIHlvdSBhYm91dCBPcGVuZG9vcuKAmXMgcmVzYWxlIHBlcmZvcm1hbmNlIGluIDIwMTc/CgojIyMjIEFuYWx5c2lzClRvIGV2YWx1YXRlIE9wZW5kb29yJ3MgcGVyZm9ybWFuY2UsIHdlIGZpcnN0IG5lZWQgdG8gZXN0YWJsaXNoIGhvdyBPcGVuZG9vcidzIGNvcmUgYnVzaW5lc3MgbW9kZWwgb3BlcmF0ZXMuIE9wZW5kb29yIGlzIGEgdHdvLXNpZGVkIG1hcmtldHBsYWNlLiBJdCdzIHN1cHBseSBzaWRlIGFsbG93cyBjb25zdW1lcnMgdG8gc2VsbCB0aGVpciBleGlzdGluZyBob21lIGZvciBhIH42JSBmZWUgd2l0aG91dCB0aGUgaGFzc2xlL2RlbGF5IG9mIHdvcmtpbmcgd2l0aCBhIHRyYWRpdGlvbmFsIGFnZW50LiBPcGVuZG9vciB0eXBpY2FsbHkgd2lsbCBtYWtlIG1vZGVzdCBpbXByb3ZlbWVudHMgKHRvIGJvb3N0IGhvbWUgdmFsdWUpIGJlZm9yZSBsaXN0aW5nIHRoZSBwdXJjaGFzZWQgaG9tZSBmb3IgcmVzYWxlLiBJdCdzIGRlbWFuZCBzaWRlIGFsbG93cyBjb25zdW1lcnMvcmVhbHRvcnMgdG8gdmlldyBPcGVuZG9vciBob21lIGxpc3RpbmdzIGluIGFuIGFwcC9icm93c2VyIGFuZCBwdXJjaGFzZSBob21lcyB0aGV5IGFyZSBpbnRlcmVzdGVkIGluLiBGb3IgdGhpcyBxdWVzdGlvbiwgd2Ugd2lsbCBpZ25vcmUgYW55IG9mIHRoZSBwb3RlbnRpYWwgYnVzaW5lc3MgbGluZSBleHRlbnNpb25zIChmaW5hbmNpbmcsIHNlcnZpY2VzLCBldGMpIGFuZCBmb2N1cyBvbmx5IG9uIHRoZSBtYXJrZXRwbGFjZSBkeW5hbWljcy4KCldlIHdpbGwgZXZhbHVhdGUgT3BlbmRvb3IncyAyMDE3IHBlcmZvcm1hbmNlIGF0IGJvdGggYWdncmVnYXRlIGFuZCBjb2hvcnQgbGV2ZWxzLgoKIyMjIyBBZ2dyZWdhdGUgTGV2ZWwKKipMaXN0aW5nIEdyb3d0aCoqOiBBY3Jvc3MgYWxsIFpJUCBjb2RlcyBhbmQgcHJpY2UgYmFuZHMsIE9wZW5kb29yIGxpc3RpbmdzIGNvbnRyYWN0ZWQgNDUlIGZyb20gNDMwIGxpc3RpbmdzIG9uIDEvNy8xNyB0byAyMzUgbGlzdGluZ3Mgb24gMS83LzE4LiBPdmVyIHRoZSBzYW1lIG9uZSB5ZWFyIHBlcmlvZCwgTUxTIGxpc3RpbmdzIGNvbnRyYWN0ZWQganVzdCAyMiUgZnJvbSA0LDM2NCB0byAzLDM4NS4gV2hpbGUgdGhpcyByZWxhdGl2ZSBkZWNsaW5lIGlzIHVubGlrZWx5IGEgc2lnbiBvZiBvdmVycGVyZm9ybWFuY2UsIGl0J3Mgbm90IGNvbmNsdXNpdmVseSB1bmRlcnBlcmZvcm1hbmNlLiBUaGUgZGVjbGluZSBjb3VsZCBhbHNvIGJlIGF0dHJpYnV0YWJsZSB0byBhIHN0cmF0ZWdpYyBkZWNpc2lvbi4gCgoqKlZpc2l0b3IgR3Jvd3RoKio6IEFjcm9zcyBhbGwgWklQIGNvZGVzIGFuZCBwcmljZSBiYW5kcywgT3BlbmRvb3IgaG9tZSB2aXNpdHMgZHJvcHBlZCAyNSUgZnJvbSAyLDU1NiB2aXNpdHMgb24gMS83LzE3IHRvIDE5MDUgbGlzdGluZ3Mgb24gMS83LzE4LiBPdmVyIHRoZSBzYW1lIHBlcmlvZCwgYXZlcmFnZSB2aXNpdG9ycy9saXN0aW5nIGluY3JlYXNlZCBmcm9tIDUuOSB0byA4LjEgKDM3JSkuIFBhcnQgb2YgdGhpcyB3YXMgbGlrZWx5IGR1ZSB0byBhIHJlZHVjdGlvbiBpbiBPRCBob3VzaW5nIGxpc3RpbmdzLCBjb25jZW50cmF0aW5nIHBvdGVudGlhbCBidXllcnMgaW4gZmV3ZXIgaG9tZXMuIEhvd2V2ZXIsIGl0J3MgcG9zc2libGUgdGhpcyBpbmRpY2F0ZXMgT3BlbmRvb3IgaXMgc2VsZWN0aW5nIG1vcmUgYXR0cmFjdGl2ZSBvZmZlcmluZ3MuCgoqKkNvbnRyYWN0IENvbnZlcnNpb24qKjogQWNyb3NzIGFsbCBaSVAgY29kZXMgYW5kIHByaWNlIGJhbmRzLCBPcGVuZG9vcidzIG1lZGlhbiBjb250cmFjdCBjb252ZXJzaW9uIHJhdGUgaXMgMi45NCUgdmVyc3VzIHRoZSBvdmVyYWxsIE1MUyByYXRlIG9mIDMuMjIlLiBUaGlzIDglIGRlbHRhIGlzIGZhaXJseSBzbWFsbCwgZXNwZWNpYWxseSBjb25zaWRlcmluZyB0aGUgMTEuNXggZGlmZmVyZW5jZSBpbiBzYW1wbGUgc2l6ZSAoT0QgY29udHJhY3RzOiAzLDY2OCB2cy4gTUxTIGNvbnRyYWN0czogNDIsMzY0KS4KCiMjIyMgQ29ob3J0IExldmVsClNwbGl0dGluZyB0aGUgZGF0YSBieSBaSVAgQ29kZSAoNCBsZXZlbHMpLCBQcmljZSBMZXZlbCAoMiBsZXZlbHMpLCBhbmQgUXVhcnRlciAoNCBsZXZlbHMpIHJldmVhbHMgaW50ZXJlc3RpbmcgdHJlbmRzLiBUaGlzIHNwbGl0IGltcGxpZXMgMzIgZGlzdGluY3QgY29ob3J0cy4gCgoqKkxpc3RpbmcgR3Jvd3RoKio6IEJldHdlZW4gMS83LzE3IGFuZCAxLzcvMTgsIE9wZW5kb29yIGV4cGVyaWVuY2VkIHNpZ25pZmljYW50IGxpc3RpbmcgZ3Jvd3RoICh+MTAwJSkgaW4gbGlzdGluZ3MgPCQyMDBLLiBUaGlzIHRyZW5kIGhvbGRzIGFjcm9zcyBhbGwgWklQIENvZGVzLiBJbiBjb250cmFzdCwgb3ZlcmFsbCBNTFMgbGlzdGluZyBjb3VudCBkZWNyZWFzZWQgY29uc2lkZXJhYmx5ICh+NDUlKSBvdmVyIHRoZSBzYW1lIHBlcmlvZCBmb3IgbGlzdGluZ3MgPCQyMDBrIChhZ2Fpbiwgc2ltaWxhciBhY3Jvc3MgYWxsIFpJUCBjb2RlcykuIENvbnZlcnNlbHksIGZvciBsaXN0aW5ncyAkMjAwSyssIHRoZSBvcHBvc2l0ZSB0cmVuZCBob2xkcy4gT3BlbmRvb3IncyBhZ2dyZWdhdGUgJDIwMCsgbGlzdGluZyBjb3VudCBwbHVtbWV0ZWQgNzglIG92ZXIgdGhlIHllYXIuIE1lYW53aGlsZSwgTUxTIGxpc3RpbmdzIGdyZXcgbW9kZXN0bHkgZm9yIHRoZSBmaXJzdCB0aHJlZSBxdWFydGVycyBvZiB0aGUgeWVhciwgYmVmb3JlIGRyb3BwaW5nIG9mZiBpbiBRNC4gT3ZlcmFsbCwgdGhlcmUgd2FzIGEgMTAlIHJlZHVjdGlvbiBpbiBNTFMgbGlzdGluZyB2b2x1bWUgZnJvbSB0aGUgc3RhcnQgb2YgdGhlIHllYXIgdG8gdGhlIGVuZC4gT3BlbmRvb3IncyBsaXN0aW5ncyBkZWNsaW5lIGFjY2VsZXJhdGVkIGluIFE0IHRvbywgaW1wbHlpbmcgdGhpcyB0cmVuZCB3YXMgbGlrZWx5IG1hcmtldCBkcml2ZW4uIEZpbmFsbHksIHRoZSByYXRpbyBvZiBPcGVuZG9vciBsaXN0aW5ncyB0byB0b3RhbCBNTFMgbGlzdGluZ3MgYXMgYSBwZXJjZW50IHZhcmllZCBkcmFtYXRpY2FsbHkgYWNyb3NzIHRoZSB0d28gcHJpY2UgbGV2ZWxzLiBJbiAyMDE3LCBPcGVuZG9vciBleHBlcmllbmNlZCA0eCByZWxhdGl2ZSBncm93dGggaW4gPCQyMDBLIHZzLiAzeCByZWxhdGl2ZSBjb250cmFjdGlvbiBpbiAkMjAwSysgKGFnYWluLCB0cmVuZCBob2xkcyBhY3Jvc3MgYWxsIFpJUCBjb2RlcykuIEJ5IHRoZSBlbmQgb2YgdGhlIHllYXIsIE9wZW5kb29yIGNvbXByaXNlZCAyMCUgb2YgdGhlIDwkMjUwSyBtYXJrZXQuIFRoZSA1IGdyYXBocyBiZWxvdyBoZWxwIHZpc3VhbGl6ZSB0aGVzZSB0cmVuZHMuIFRoZSBncmFwaHMgZG8gbm90IGFwcGVhciB0byBzaG93IHNpZ25pZmljYW50IHNlYXNvbmFsaXR5LCBleGNlcHQgZm9yIHRoZSBkaXAgYXJvdW5kIHRoZSBob2xpZGF5cy4gQWxsIDQgbGlzdGluZyBjaGFydHMgc2hvdyBhbiBhZGRpdGlvbmFsIGRpcCBhcm91bmQgVGhhbmtzZ2l2aW5nIHRoYXQgbGFzdHMgdGhyb3VnaCBOZXcgWWVhcnMsIGJlZm9yZSB0dXJuaW5nIHNoYXJwbHkgdXAgYWdhaW4uIFRoaXMgc2VlbXMgbm9ybWFsIGFuZCByZWZsZWN0cyByZWFsIHdvcmxkIGR5bmFtaWNzLiAKYGBge3J9CiAgICAgIGdncC51biArCiAgICAgICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yPXppcF9jb2RlLHk9bWxzX2xpc3RpbmdzKSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIkxpc3RpbmcgQ291bnQiLCB0aXRsZSA9ICJNTFMgTGlzdGluZyBDb3VudDogPCQyMDBLIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCmBgYApgYGB7cn0KICAgICAgZ2dwLnVuICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1vZF9saXN0aW5ncykpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJMaXN0aW5nIENvdW50IiwgdGl0bGUgPSAiT0QgTGlzdGluZyBDb3VudDogPCQyMDBLIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCmBgYApgYGB7cn0KICAgICAgZ2dwLm92ICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1tbHNfbGlzdGluZ3MpKSsKICAgICAgICBsYWJzKHggPSAiVGltZSIsIHkgPSAiTGlzdGluZyBDb3VudCIsIHRpdGxlID0gIk1MUyBMaXN0aW5nIENvdW50OiAkMjAwSysgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKYGBgCmBgYHtyfQogICAgICBnZ3Aub3YgKwogICAgICAgIGdlb21faml0dGVyKGFlcyhjb2xvcj16aXBfY29kZSx5PW9kX2xpc3RpbmdzKSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIkxpc3RpbmcgQ291bnQiLCB0aXRsZSA9ICJPRCBMaXN0aW5nIENvdW50OiAkMjAwSysgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7cn0KICAgICAgZ2dwIDwtIGdncGxvdChkYXRhLmZyYW1lKGRhdGEpLCBhZXMoZGF0ZSwxMDAqb2RfbWxzX2xpc3RpbmdfcmF0aW8pKQogICAgICBnZ3AgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcHJpY2VfYmFuZCkpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJQZXJjZW50IiwgdGl0bGUgPSAiT0Q6TUxTIExpc3RpbmcgUmF0aW8gKEFsbCBaaXAgQ29kZXMpIikrCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKQpgYGAKCioqVmlzaXRvciBHcm93dGgqKjogVmlzaXRvciBncm93dGggcGVyIGxpc3RpbmcgdHJlbmRzIHdlcmUgY29uc2lzdGVudCBhY3Jvc3MgYWxsIFpJUCBjb2Rlcy4gQXQgYW4gYWdncmVnYXRlIGxldmVsLCB2aXNpdG9ycyBwZXIgbGlzdGluZyBncmV3IDM3JSBvdmVyIHRoZSB5ZWFyLiBIb3dldmVyLCB0aGUgdHJlbmRzIHZhcmllZCBzdWJzdGFudGlhbGx5IGJldHdlZW4gdGhlIHR3byBwcmljZSBsZXZlbHMuIFZpc2l0b3JzIHBlciA8JDIwMEsgbGlzdGluZyBmZWxsIDI1JSBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIHllYXIuIE1lYW53aGlsZSwgZm9yIGxpc3RpbmdzICQyMDBLKywgdmlzaXRzIHBlciBsaXN0aW5nIGdyZXcgb3ZlciA2MCUgZHVyaW5nIHRoZSB5ZWFyIChmcm9tIDUuMiB0byA4LjUpLiBJbiBhZ2dyZWdhdGUsIHRoZSB0b3RhbCBudW1iZXIgb2YgZGFpbHkgdmlzaXRzIGRpdmlkZWQgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBkYWlseSBsaXN0aW5ncyB3ZXJlIDkuNiBhbmQgNi40LCByZXNwZWN0aXZlbHksIGZvciA8JDIwMEsgYW5kICQyMDBLKy4gT3ZlciB0aGUgY291cnNlIG9mIHRoZSB5ZWFyLCBpdCB0b29rLCBpbiBhZ2dyZWdhdGUgYW5kIG9uIGF2ZXJhZ2UsIDE4NyB2aXNpdHMgdG8gZ2VuZXJhdGUgYSBjb250cmFjdCBmb3IgYSA8JDIwMEsgbGlzdGluZyBhbmQgMjk4IHZpc2l0cyB0byBnZW5lcmF0ZSBhIGNvbnRyYWN0IGZvciBhICQyMDBLKyBsaXN0aW5nLiBUaGUgdHdvIGdyYXBocyBiZWxvdyBoZWxwIHZpc3VhbGl6ZSB0aGVzZSB0cmVuZHMuIEZ1cnRoZXJtb3JlLCBjb21wYXJpbmcgdGhlc2UgZ3JhcGhzIHRvIHRoZSBsaXN0aW5nIGdyYXBocyBhYm92ZSBzaG93cyBhIGhpZ2ggZGVncmVlIG9mIChpbnZlcnNlKSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZpc2l0b3JzIGFuZCBsaXN0aW5ncy4gR2l2ZW4gdGhlcmUgaXMgbGlrZWx5IGEgcmVhc29uYWJseSBzdGFibGUgc3VwcGx5IG9mIHBvdGVudGlhbCBob21lIGJ1eWVycyBpbiB0aGUgYXJlYSwgaXQncyBsaWtlbHkgdGhhdCBhcyB0aGUgbnVtYmVyIG9mIGhvbWVzIGRlY3JlYXNlcyB0aGUgbnVtYmVyIG9mIHZpc2l0cyBpbmNyZWFzZXMgYXMgaG9tZSBidXllcnMgYXJlIGVpdGhlciBzcHJlYWQgbGVzcyB0aGluIG9yIGhhdmUgbGVzcyBvcHRpb25zIHRvIGZpbHRlciBvbi4gQ29udmVyc2VseSwgd2l0aCBtYW55IG9mZmVyaW5ncywgdGhlIHZpc2l0cyBsaWtlbHkgZ2V0IHNwcmVhZCBhY3Jvc3MgYSBsYXJnZXIgYmFzZS4gCmBgYHtyfQogICAgICBnZ3AudW4gKwogICAgICAgIGdlb21faml0dGVyKGFlcyhjb2xvcj16aXBfY29kZSx5PW9kX3Zpc2l0c19saXN0aW5nKSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIkNvdW50IiwgdGl0bGUgPSAiVmlzaXRzIFBlciBMaXN0aW5nOiA8JDIwMEsgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKYGBgCmBgYHtyfQogICAgICBnZ3Aub3YgKwogICAgICAgIGdlb21faml0dGVyKGFlcyhjb2xvcj16aXBfY29kZSx5PW9kX3Zpc2l0c19saXN0aW5nKSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIkNvdW50IiwgdGl0bGUgPSAiVmlzaXRzIFBlciBMaXN0aW5nOiAkMjAwSysgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKKipDb250cmFjdCBDb252ZXJzaW9uKio6IExvb2tpbmcgYXQgYWdncmVnYXRlIHBlcmZvcm1hbmNlIGFjcm9zcyBlYWNoIG9mIHRoZSA0IHF1YXJ0ZXJzLCBPcGVuZG9vcidzIG1lZGlhbiBjb250cmFjdCBjb252ZXJzaW9uIHJhdGUgcGVyZm9ybWFuY2Ugd2FzIG9uIHBhciB3aXRoIHRoZSBvdmVyYWxsIE1MUyBtYXJrZXQuIE9wZW5kb29yIGxhZ2dlZCBieSAxJSBwb2ludCBpbiBRMSwgZXhjZWVkIGJ5IDAuMjUlIHBvaW50IGluIFEyLCBhbmQgd2FzIHZpcnR1YWxseSBpZGVudGljYWwgaW4gUTMgYW5kIFE0LiBBY3Jvc3MgYWxsIDMyIGNvaG9ydHMsIE9wZW5kb29yIG91dHBlcmZvcm1lZCBNTFMgaW4gY29udHJhY3QgY29udmVyc2lvbiByYXRlIDE5IHRpbWVzIChvdXQgb2YgMzIgcG9zc2libGUpLiBPbiBhbiBhbm51YWwgYmFzaXMsIGFtb25nIHRoZSA4IGNvaG9ydHMgKDQgWklQcyAqIDIgUHJpY2VzKSwgT3BlbmRvb3Igb3V0cGVyZm9ybWVkIDQgdGltZXMgYW5kIHVuZGVycGVyZm9ybWVkIDQgdGltZXMuIE9wZW5kb29yIHdhcyBtb3JlIGxpa2VseSB0byBvdXRwZXJmb3JtIGluIHRoZSA8JDIwMEsgcHJpY2UgbGV2ZWwgdGhhbiBpbiB0aGUgJDIwMEsrIG9uZSAoMyBvZiBpdCdzIDQgb3V0cGVyZm9ybXMgd2VyZSBpbiA8JDIwMEspLiBPcGVuZG9vciBpcyBkZWNpZGVseSBiZXR0ZXIgdGhhbiBNTFMgYXZlcmFnZSBpbiBaSVAgQ29kZSBBIGFuZCBkZWNpZGVseSB3b3JzZSBpbiBaSVAgQ29kZSBCLiBPcGVuZG9vciB0ZW5kcyB0byBvdXRwZXJvZnJtIGluIFpJUCBDIGFuZCBELCBleGNlcHQgZm9yIFExIGluIDwkMjAwSyBhbmQgaW4gUTQgZm9yICQyMDBLKywgd2hlbiBkYXRhIGFyZSB0b28gdGhpbiB0byBwcm9wZXJseSBhc3Nlc3MuICAKClRoZSB0d28gZ3JhcGhzIGJlbG93IHNob3cgdGhlIGRpZmZlcmVuY2UgaW4gT0QgdnMuIE1MUyBjb250cmFjdCBjb252ZXJzaW9uIHJhdGUuIFRoZSBibGFjayBsaW5lIGlzIDAsIHNvIGFueXRoaW5nIGFib3ZlIHNob3dzIE9wZW5kb29yIGNvbnZlcnRpbmcgYmV0dGVyIHRoYW4gTUxTLiBUaGUgbGVmdCBzY2FsZSBpcyBwZXJjZW50YWdlIHBvaW50cy4KYGBge1J9CiAgICAgICMjIDwkMjAwSyBzdWJzZWN0aW9uCiAgICAgICAgZ2dwLmNvaG9ydHMgKyAKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkEuLjIwMEspLCBjb2xvciA9ICIjRjg3NjZEIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQS4uMjAwSyksIGNvbG9yID0gIiNGODc2NkQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkIuLjIwMEspLCBjb2xvciA9ICIjN0NBRTAwIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQi4uMjAwSyksIGNvbG9yID0gIiM3Q0FFMDAiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkMuLjIwMEspLCBjb2xvciA9ICIjMDBDRkI0IikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQy4uMjAwSyksIGNvbG9yID0gIiMwMENGQjQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkQuLjIwMEspLCBjb2xvciA9ICIjQzc3Q0ZGIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqRC4uMjAwSyksIGNvbG9yID0gIiNDNzdDRkYiKSsKICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh5PTApLCBjb2xvcj0iQmxhY2siKSsKICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikrCiAgICAgICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiRGlmZmVyZW5jZTogUGVyY2VudGFnZSBQb2ludHMiLCB0aXRsZSA9ICJEaWZmZXJlbmNlIGluIE9EOk1MUyBDb250cmFjdC9MaXN0aW5nIFdpbiBSYXRlOiA8JDIwMEssIEJ5IFF1YXJ0ZXIiKQpgYGAKYGBge1J9CiAgICAgICMjICQyMDBLKyBzdWJzZWN0aW9uCiAgICAgICAgZ2dwLmNvaG9ydHMgKyAKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkEuMjAwSy4pLCBjb2xvciA9ICIjRjg3NjZEIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQS4yMDBLLiksIGNvbG9yID0gIiNGODc2NkQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkIuMjAwSy4pLCBjb2xvciA9ICIjN0NBRTAwIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQi4yMDBLLiksIGNvbG9yID0gIiM3Q0FFMDAiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkMuMjAwSy4pLCBjb2xvciA9ICIjMDBDRkI0IikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQy4yMDBLLiksIGNvbG9yID0gIiMwMENGQjQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkQuMjAwSy4pLCBjb2xvciA9ICIjQzc3Q0ZGIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqRC4yMDBLLiksIGNvbG9yID0gIiNDNzdDRkYiKSsKICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh5PTApLCBjb2xvciA9ICJCbGFjayIpKwogICAgICAgICAgbGFicyh4ID0gIlF1YXJ0ZXIiLCB5ID0gIkRpZmZlcmVuY2U6IFBlcmNlbnRhZ2UgUG9pbnRzIiwgdGl0bGUgPSAiRGlmZmVyZW5jZSBpbiBPRDpNTFMgQ29udHJhY3QvTGlzdGluZyBXaW4gUmF0ZTogJDIwMEsrLCBCeSBRdWFydGVyIikKYGBgCgoKIyMjIyBDb25jbHVzaW9uCkF0IGFuIGFnZ3JlZ2F0ZSBsZXZlbCwgaXQncyBpbmNvbmNsdXNpdmUgaWYgT3BlbmRvb3IgaXMgdW5kZXIgb3Igb3ZlcnBlcmZvcm1pbmcuIExpc3RpbmdzIGFyZSBkb3duIDQ1JSBhbmQgdmlzaXRzIGFyZSBkb3duIDI1JSwgeWV0IHZpc2l0cy9saXN0aW5nIGFyZSB1cCAzNyUgYW5kIGF2ZXJhZ2UgY29udmVyc2lvbiByYXRlIGRpZmZlcmVuY2UgaXMgb25seSBtYXJnaW5hbGx5IGxvd2VyICg4JSkuIElmIGZvcmNlZCB0byBtYWtlIGEgY29uY2x1c2lvbiBvbiB0aGlzIGRhdGEgYWxvbmUsIEknZCBsZWFuIHRvd2FyZCB1bmRlcnBlcmZvcm1hbmNlLgoKTG9va2luZyBhdCB0aGUgY29ob3J0IGxldmVsIGRhdGEgcHJvdmlkZXMgZ3JlYXRlciBjbGFyaXR5LiBPcGVuZG9vciBvdmVycGVyZm9ybWVkIGluIHRoZSA8JDIwMEsgY2F0ZWdvcnksIGdyb3dpbmcgbGlzdGluZ3MgbmVhcmx5IDJ4IG92ZXIgdGhlIHllYXIgd2hpbGUgb25seSBsZXR0aW5nIHZpc2l0b3JzL2xpc3Rpbmcgc2xpZGUgMjUlLiBXaGlsZSBpdCdzIGNvbnRyYWN0IGNvbnZlcnNpb24gcmF0ZSBzdGVhZGlseSBkZWNyZWFzZWQgb3ZlciB0aGUgeWVhciAocG90ZW50aWFsbHkgZHVlIHRvIGluY3JlYXNpbmcgc3VwcGx5KSwgaXQgc3RpbGwgbWFpbnRhaW5lZCBhIGhpZ2hlciBtZWRpYW4gY29udmVyc2lvbiByYXRlIHRoYW4gdGhlIG92ZXJhbGwgTUxTIG1hcmtldCByYXRlLiBPcGVuZG9vcidzIHBlcmZvcm1hbmNlIGluICQyMDBLKyB1bmRlcnBlcmZvcm1lZCBzbGlnaHRseSBvdmVyIHRoZSB5ZWFyLiBMaXN0aW5ncyBzaHJ1bmsgNzglIGFuZCBtZWRpYW4gY29udHJhY3QgY29udmVyc2lvbiByYXRlIG9ubHkgaW5jcmVhc2VkIGluIFpJUHMgQSBhbmQgQi4gVGhlIG1lZGlhbiBjb250cmFjdCBjb252ZXJzaW9uIHJhdGUgZm9yIFpJUHMgQyBhbmQgRCBmZWxsIHRvIDAlIGFzIGhvbWVzIHZpcnR1YWxseSBzdG9wcGVkIHNlbGxpbmcgKGp1c3QgMTMgY29udHJhY3RzIGJldHdlZW4gdGhlIDIgWklQcyBpbiBRNC4pICpOb3RlOiBJIHB1cnBvc2VmdWxseSB1c2VkIG1lZGlhbiBmb3IgY29udHJhY3QgY29udmVyc2lvbiByYXRlIGZvciBqdXN0IHRoaXMgcmVhc29uLiBVc2luZyBhdmVyYWdlIHByb3ZpZGVzICJiZXR0ZXIiIHJlc3VsdHMsIGJ1dCBvbiBzdWNoIHRoaW4gZGF0YSwgaXQgZmVlbHMgbWlzbGVhZGluZy4gVGhlIHJlYWxpdHkgaXMgb24gbW9zdCBkYXlzIGluIFE0IE9wZW5kb29yIGlzIG5vdCBzZWxsaW5nICQyMDBLKyBob21lcyBpbiBaSVBzIEMgYW5kIEQuKgoKIyMjIFEyLiBIeXBvdGhlc2VzIG9uIFBlcmZvcm1hbmNlIFRyZW5kcwojIyMjIFByb21wdApCYXNlZCBvbiB3aGF0IHlvdSBsZWFybmVkIGluIFF1ZXN0aW9uIDEsIHdoYXQgZG8geW91IGh5cG90aGVzaXplIGlzIGRyaXZpbmcgYW55IG92ZXIvdW5kZXJwZXJmb3JtYW5jZSB0cmVuZHM/IEZvciBlYWNoIGh5cG90aGVzaXMsIHdoYXQgYWRkaXRpb25hbCBkYXRhIHdvdWxkIHlvdSByZXF1ZXN0IGFuZCB3aGF0IGFuYWx5c2VzIHdvdWxkIHlvdSBydW4gdG8gdmFsaWRhdGU/CgojIyMjIEFuYWx5c2lzCioqSHlwb3RoZXNpcyAjMTogT3BlbmRvb3IgaXMgYmlkZGluZyBkaWZmZXJlbnRseSBvbiA8JDIwMEsgYW5kICQyMDBLKyBIb21lcyoqOiBBbiBpbnRyaWd1aW5nIGRhdGEgcG9pbnQgaXMgdGhlIHJhdGlvIG9mICQyMDBLKyBsaXN0aW5ncyB0byA8JDIwMEsgbGlzdGluZ3MuIE9wZW5kb29yIGJlZ2lucyAyMDE3IHdpdGggYSByYXRpbyBvZiA0OjEgKDQgJDIwMEsgOiAxIDwkMjAwSykgd2hpbGUgZ2VuZXJhbCBNTFMgbGlzdGluZ3MgYXJlIGF0IDI6MS4gRHVyaW5nIFExLCBPcGVuZG9vciBhY2NlbGVyYXRlcyB0byBhIHBlYWsgb2YgODoxLCBiZWZvcmUgcHJlY2lwaXRvdXNseSBkZWNsaW5pbmcgdG8gYSByYXRpbyBvZiAxOjIgYXQgdGhlIGVuZCBvZiB0aGUgeWVhciAoYSBmYWN0b3Igb2YgMTZ4KSEgSW4gY29tcGFyaXNvbiwgTUxTIGxpc3RpbmdzIGdyb3cgZnJvbSAyOjEgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2FtcGxlIHNldCB0byBhIHBlYWsgb2YgNDoxIGF0IG1pZC15ZWFyLCBiZWZvcmUgc2V0dGxpbmcgYXQgMy4yNToxLiBBZ2FpbiwgZ2l2ZW4gdGhlIHNhbXBsZSBzaXplcyBhcmUgaGlnaGx5IHNrZXdlZCAoMTEuNToxKSwgaXQncyBub3Qgc3VycHJpc2luZyB0byBzZWUgYSBsYXJnZXIgc3dpbmcgaW4gT3BlbmRvb3IncyByYW5nZS4gSG93ZXZlciwgc3VjaCBhIHJldmVyc2FsIGxpa2VseSBpbXBsaWVzIGEgdHJlbmQgd29ydGggZXhhbWluaW5nLiBNeSBoeXBvdGhlc2lzIGlzIHRoYXQgT3BlbmRvb3Igc2VlcyBhbiBlY29ub21pYyBvcHBvcnR1bml0eSBpbiBob21lcyA8JDIwMEsuIEhvbWVzIDwkMjAwayBjb21wcmlzZSB+MzAlIG9mIHRoZSBvdmVyYWxsIG1hcmtldCAoc2ltaWxhciBhY3Jvc3MgYWxsIFpJUHMpLCBuYXR1cmFsbHkgaW1wbHlpbmcgdGhlIG1lZGlhbiBob21lIHByaWNlIGlzIGFib3ZlICQyNTBLLiBQZXJoYXBzIE9wZW5kb29yIGhhcyBkZXZlbG9wZWQgYSBzdHJhdGVneSBmb3IgaW5jcmVhc2luZyB0aGUgdmFsdWUgb2YgaG9tZXMgPCQyNTBLIG1vcmUgc3Vic3RhbnRpYWxseSB0aGFuIHRoZSBtZWRpYW4gaG9tZS4gSWYgdGhpcyBpcyB0aGUgY2FzZSwgT3BlbmRvb3IgbWF5IGJlIGZvY3VzaW5nIG9uIHRoZSBsb3cgZW5kIG9mIHRoZSBtYXJrZXQgd2hlcmUgaXQgc2VlcyBhIGdyZWF0ZXIgb3Bwb3J0dW5pdHkgZm9yIGdyb3NzIHByb2ZpdC4gCgpUbyBjb25maXJtIHRoaXMgaHlwb3RoZXNpcywgSSdkIHJlcXVlc3QgdGhlIGZvbGxvd2luZyBkYXRhOgoKICAgIC0gTWVkaWFuIGhvbWUgcHJpY2UgYWNyb3NzIGFsbCBaSVBzCiAgICAtIEFjdHVhbCBwcmljaW5nIGRhdGEgZm9yIGFsbCBPcGVuZG9vciBob21lIHB1cmNoYXNlcwogICAgLSBBY3R1YWwgcHJpY2luZyBkYXRhIGZvciBhbGwgT3BlbmRvb3IgaG9tZSBzYWxlcwogICAgLSBBY3R1YWwgaG9tZSBkYXRhIChCZWRzLCBiYXRocywgc3EuIGZ0LCB5ZWFyIGJ1aWx0LCBldGMpCiAgICAtIEFjdHVhbCBjb3N0IGRhdGEgZm9yIGFueXRoaW5nIGludm9sdmVkIGluIHRoZSByZW1vZGVsL3NhbGUgKHRvIGVzdGFibGlzaCBHcm9zcyBQcm9maXQpCiAgICAtIEFjdHVhbCB2aXNpdG9yIGRhdGEgYXQgaG91c2luZyB1bml0IGxldmVsLCBub3QganVzdCBkYWlseSBsZXZlbAogICAgLSBBY3R1YWwgdGltZSBvbiBtYXJrZXQgZm9yIGVhY2ggbGlzdGluZyAoYm90aCBPRCBhbmQgTUxTKQogICAgLSBPdmVyYWxsIFJhdGUgb2YgT3BlbmRvb3Igb2ZmZXIgYWNjZXB0YW5jZS9kZWNsaW5lIGZvciBob21lcyA8JDI1MEsgdnMuIGhvbWVzICQyNTBLKyAKICAgIC0gRGVsdGEgb2YgSW50ZXJuYWwgRmFpciBNYXJrZXQgVmFsdWF0aW9uIG1ldHJpY3MgYW5kIGFkdmVydGlzZWQgb2ZmZXIgdG8gcG90ZW50aWFsIGhvbWUgc2VsbGVycwogICAgLSBFc3RpbWF0ZXMgb24gYWRqYWNlbnQgaG9tZSBwcmljZSAvLyBoaXN0b3JpY2FsIGRhdGEgb24gYWRqYWNlbnQgaG9tZSBzYWxlcwogICAgLSBBZ2dyZWdhdGUgT3BlbmRvb3IgZmluYW5jaWFsIGRhdGEgZm9yIHRoZSA0IFpJUCBDb2RlcyAKCkZyb20gaGVyZSwgSSdkIHJ1biBhbiBhbmFseXNpcyB0byBzZWUgaWYgT3BlbmRvb3IgaXMgaW4gZmFjdCBjcmVhdGluZyBtb3JlIGdyb3NzIHByb2ZpdC9ob21lIGluIHRoZSA8JDI1MEsgYnJhY2tldC4gSSdkIGNvbnN0cnVjdCB1bml0IGVjb25vbWljcyBvbiBhbiBpbmRpdmlkdWFsIGhvdXNpbmcgbGV2ZWwgYW5kIHRoZW4gY2FsY3VsYXRlIElSUiBiYXNlZCBvbiB0aW1pbmcgaW5mb3JtYXRpb24gcmVsYXRlZCB0byB0aGUgc2FsZS4gSSdkIGFsc28gYmUgY3VyaW91cyB0byB1bmRlcnN0YW5kIHRoZSBiaWQgYWNjZXB0YW5jZSByYXRlIGZvciB0aGVzZSBjaGVhcGVyIGhvbWVzIC0tIHBvdGVudGlhbGx5IHRoZXJlIGlzIG1vcmUgY3VzdG9tZXIgYXBwZXRpdGUgdG8gc2VsbCBhdCBhIGRpc2NvdW50LiBTbyBJZiBteSBwcmVkaWN0aW9uIGlzIHJpZ2h0LCB0aGUgdW5pdCBlY29ub21pY3Mgd2lsbCBiZSBoaWdoZXIsIGFuZCB0aGUgdGltZSBvbiBtYXJrZXQgd2lsbCBiZSBsb3dlciBmb3IgdGhlc2UgaW1wcm92ZWQgT3BlbmRvb3IgbGlzdGluZ3MuIEknZCBiZSBjdXJpb3VzIHRvIGNoZWNrIGlmIHRoZSBwcmljZSBvZiBzdXJyb3VuZGluZyBob21lcyB3YXMgcHJlZGljdGl2ZSBvbiBldmVudHVhbCBzYWxlIHByaWNlIGFmdGVyIHJlbW9kZWwuIFBlcmhhcHMgbGlnaHQgZmFjYWRlIHdvcmsgb24gYSB3b3JuIGhvdXNlIG9uIGEgbmljZSBzdHJlZXQvYmV0d2VlbiBuaWNlciBob21lcyBoYXMgdGhlIGFiaWxpdHkgdG8gY3JlYXRlIGV4Y2VzcyBncm9zcyBwcm9maXQgKG9yIG5vdCE/KS4gRWl0aGVyIHdheSwgdGhpcyBkYXRhIHNlZW1zIHRvIGltcGx5IGEgc3lzdGVtYXRpYyBzdHJhdGVneSBhdCB0aGUgbG93IGVuZC4KYGBge3J9CiAgICAgIHBhcihtZnJvdz1jKDEsMikpCiAgICAgICMjIE9wZW5kb29yCiAgICAgIHJhdGlvXzIwMEsub2QgPC0gb3YuZGF0YSRvZF9saXN0aW5ncy91bi5kYXRhJG9kX2xpc3RpbmdzCiAgICAgIHBsb3QocmF0aW9fMjAwSy5vZCxtYWluID0gIk9EIExpc3Q6IFJhdGlvICQyMDBLKy88JDIwMEsgIiwgeGxhYiA9ICJUaW1lIiwgeWxhYiA9ICJSYXRpbyIsIGNvbCA9ICJkb2RnZXJibHVlMiIpCiAgICAgICMjIE1MUwogICAgICByYXRpb18yMDBLLm1scyA8LSBvdi5kYXRhJG1sc19saXN0aW5ncy91bi5kYXRhJG1sc19saXN0aW5ncwogICAgICBwbG90KHJhdGlvXzIwMEsubWxzLG1haW4gPSAiTUxTIExpc3Q6IFJhdGlvICQyMDBLKy88JDIwMEsgIiwgeGxhYiA9ICJUaW1lIiwgeWxhYiA9ICJSYXRpbyIsIGNvbCA9ICJncmV5NzUiKQpgYGAKCioqSHlwb3RoZXNpcyAjMjogT3BlbmRvb3IgaXMgcHJlZmVycmVkIGFtb25nIHlvdW5nZXIgYnV5ZXJzICYgZmlyc3QgdGltZSBidXllcnMvc2VsbGVycyoqOiBUaGUgdHJhbnNpdGlvbiBmcm9tIGhpZ2hlciBwcmljZWQgaW52ZW50b3J5IHRvIGxvd2VyIHByaWNlZCBpbnZlbnRvcnkgbWF5IGJlIHJlZmxlY3RpdmUgb2YgT3BlbmRvb3IgdXNlciBwcmVmZXJlbmNlcy4gQXMgYSB0ZWNoIHByb2R1Y3QsIHBvdGVudGlhbGx5IE9wZW5kb29yIGlzIG1vcmUgcG9wdWxhciBhbmQgdHJ1c3RlZCBhbW9uZyBhIHlvdW5nZXIgYXVkaWVuY2UuIEdlbmVyYWxseSBzcGVha2luZywgeW91bmdlciBidXllcnMgYXJlIGVhcmxpZXIgaW4gdGhlaXIgY2FyZWVyIHdpdGggbGVzcyBhbWFzc2VkIHNhdmluZ3Mvd2VhbHRoLiBJZiBPcGVuZG9vciBpcyBpbmRlZWQgcG9wdWxhciB3aXRoIGZpcnN0IHRpbWUgYnV5ZXJzL3NlbGxlcnMsIHRoZW4gaXQgd291bGQgbWFrZXMgc2Vuc2UgdG8gY2F0ZXIgdG8gdGhlIGxvd2VyIGVuZCBvZiB0aGUgbWFya2V0LiBUaGVzZSB1c2VycyBtYXkgYWxzbyBoYXZlIGEgbG93ZXIgQ0FDIGFuZCBoaWdoZXIgcG90ZW50aWFsIExUViwgYXMgdGhleSB3aWxsIGJlIG1vcmUgbGlrZWx5IHRvIGVuZ2FnZSBpbiBtdWx0aXBsZSBmdXR1cmUgc2FsZXMvcHVyY2hhc2VzIGFzIHRoZWlyIG5lZWRzIGV2b2x2ZS4gIAoKVG8gY29uZmlybSB0aGlzIGh5cG90aGVzaXMsIEknZCByZXF1ZXN0IHRoZSBmb2xsb3dpbmcgZGF0YToKCiAgICAtIEhpc3RvcmljYWwgZGVtb2dyYXBoaWMgZGF0YSBvbiBhbGwgT3BlbmRvb3Igc2FsZXMKICAgIC0gQ3VycmVudCBkZW1vZ3JhcGhpYyBkYXRhIG9uIGN1cnJlbnQgT3BlbmRvb3IgbGlzdGluZ3MKICAgIC0gRGVtb2dyYXBoaWMgZGF0YSBvbiBPcGVuZG9vciBtb2JpbGUvd2ViIHVzZXJzIAogICAgLSBDdXN0b21lciBBY3F1aXNpdGlvbiBDb3N0IGRhdGEgCiAgICAtIEVuZ2FnZW1lbnQgYW5kIHJldGVudGlvbiBkYXRhIHcvIGNvaG9ydCBhbmFseXNpcyBieSBkZW1vZ3JhcGhpYyAKICAgIC0gVHlwZXMgb2YgaG9tZXMgc2F2ZWQvdmlld2VkIGluIGFwcCBieSBkZW1vZ3JhcGhpYwogICAgLSBSZXBvcnRlZCBpbmNvbWUgYW5kIG5ldCB3b3J0aCBieSBzZWxsZXJzIGFuZCBidXllcnMKICAgIC0gVXNlciBncm93dGggZGF0YSBhcyBhIGZ1bmN0aW9uIG9mIGhvbWUgbGlzdGluZ3MgJiBob21lIHByaWNlcwoKRnJvbSBoZXJlLCBJJ2QgcnVuIGFuIGFuYWx5c2lzIHRvIHNlZSBpZiBPcGVuZG9vciBpcyBpbiBmYWN0IHByZWZlcnJlZCBieSB5b3VuZ2VyL2xvd2VyIHdlYWx0aCBwZW9wbGUuIFRvIGRvIHRoaXMsIEknZCBsb29rIGF0IGVuZ2FnZW1lbnQgKGRpZ2l0YWwgYW5kIHBoeXNpY2FsKSByZWxhdGl2ZSB0byBhZ2Uvd2VhbHRoIGFuZCB0aWUgaXQgdG8gcHVyY2hhc2Uvc2FsZSBkYXRhIGhpc3RvcnkuIEknZCBiZSBlc3BlY2lhbGx5IGN1cmlvdXMgdG8gZXN0YWJsaXNoIHBhdHRlcm5zIG9mIGludGVudC4gRm9yIGV4YW1wbGUsIGhvdyBtYW55IGhvbWUgdmlzaXRzIGRvZXMgdGhlIGF2ZXJhZ2UgcGVyc29uIG1ha2UgYmVmb3JlIHB1cmNoYXNpbmcgYSBob3VzZS4gV2hhdCBwYXR0ZXJucyBvZiBiZWhhdmlvcnMgaW4gYXBwIChzYXZpbmcgbGlzdGluZ3M/KSBzaWduYWwgdGhlIGhpZ2ggbGlrZWxpaG9vZCBvZiBwbGFjaW5nIGFuIG9mZmVyPwoKSXQncyBhbHNvIHdvcnRoIG5vdGluZyB0aGlzIHN0cmF0ZWd5IG1heSBoZWxwIE9wZW5kb29yIGluY3JlYXNlIGl0J3MgYnJhbmQgYXdhcmVuZXNzIGF0IGxvd2VyIHRvdGFsIGNvc3QgKGJ5IGluY3JlYXNpbmcgaW52ZW50b3J5IGluIGxvd2VyIHByaWNlZCBob21lcykuIElmIHRoZSBjdXJyZW50IGZvY3VzIGlzIHVzZXIgZ3Jvd3RoIGFuZCBleHBhbnNpb24sIHRoZW4gaW5jcmVhc2luZyBpbnZlbnRvcnkgaW4gbG93ZXIgY29zdCBob21lcyB3aWxsIGxpa2VseSBhbGxvdyBPcGVuZG9vciB0byBncm93IG1vcmUgY29zdCBlZmZlY3RpdmVseS4gCgoKKipIeXBvdGhlc2lzICMzaS9paTogWklQIENvZGVzIEEgYW5kIEIgaGF2ZSB2ZXJ5IGRpZmZlcmVudCBpKSBEZW1vZ3JhcGhpY3MgLU9SLSBpaSkgT3BlbmRvb3IgTWFya2V0aW5nIFN0cmF0ZWdpZXMqKjogWklQIENvZGVzIEEgYW5kIEIgYXJlIHRoZSB0d28gbGFyZ2VzdCBncm91cHMsIHlldCBoYXZlIHRoZSBtb3N0IGRldmlhdGlvbiBpbiBjb250cmFjdCBjb252ZXJzaW9uIHBlcmZvcm1hbmNlLiBMdWNraWx5LCB0aGV5IGFyZSB0aGUgbGFyZ2VzdCBncm91cHMsIG1ha2luZyB0aGUgc2FtcGxlcyBtb3JlIHN0YWJsZS4gTGV0J3MgcXVpY2tseSByZWNhcCBjb2hvcnQgcGVyZm9ybWFuY2UgZm9yIGVhY2ggWklQIGNvZGUgYnkgcXVhcnRlciBhbmQgcHJpY2UgYmFuZCAoOCBjb2hvcnRzIGZvciBlYWNoIFpJUCkuIEluIFpJUCBDb2RlIEEsIE9EIGNvbnRyYWN0IGNvbnZlcnNpb24gcmF0ZSBleGNlZWRzIE1MUyBjb252ZXJzaW9uIHJhdGUgaW4gNyBvZiA4IGNvaG9ydHMuIEluIGNvbnRyYXN0LCBpbiBaSVAgQ29kZSBCLCBPRCB1bmRlcnBlcmZvcm1zIE1MUyBpbiA1IG9mIDggY29ob3J0cy4gRm9yIHRoZSAzIGl0IG91dHBlcmZvcm1zLCB0aGUgbWFyZ2luIGlzIHF1aXRlIHNtYWxsLiBaSVAgQ29kZSBBIGlzIE9wZW5kb29yJ3Mgc3Ryb25nZXN0IHBlcmZvcm1lciBhbmQgWklQIENvZGUgQiBpcyBpdCdzIHdlYWtlc3QuIE15IGh5cG90aGVzaXMgaXMgdGhhdCBlaXRoZXIgdGhlc2UgWklQIGNvZGVzIGhhdmUgaSkgdmVyeSBkaWZmZXJlbnQgZGVtb2dyYXBoaWNzIE9SIGlpKSBPcGVuZG9vciBoYXMgdXNlZCB0d28gdmVyeSBkaWZmZXJlbnQgbWFya2V0aW5nIHN0cmF0ZWdpZXMgaW4gdGhlIFpJUCBDb2Rlcy4KClRvIGNvbmZpcm0gdGhpcyBoeXBvdGhlc2lzLCBJJ2QgcmVxdWVzdCB0aGUgZm9sbG93aW5nIGRhdGE6CgogICAgLSBEZW1vZ3JhcGhpYyBkYXRhIG9uIGVhY2ggWklQIENvZGUgKGFnZSwgcmFjZSwgcG9saXRpY2FsLCBpbmNvbWUsIG1haW4gbGFuZ3VhZ2UsIGV0Yy4pCiAgICAtIEN1cnJlbnQgZGVtb2dyYXBoaWMgZGF0YSBvbiBjdXJyZW50IE9wZW5kb29yIGxpc3RpbmdzIGluIGVhY2ggWklQCiAgICAtIERlbW9ncmFwaGljIGRhdGEgb24gT3BlbmRvb3IgbW9iaWxlL3dlYiB1c2VycyBpbiBlYWNoIFpJUAogICAgLSBDdXN0b21lciBBY3F1aXNpdGlvbiBDb3N0IGRhdGEgZm9yIGVhY2ggWklQCiAgICAtIEVuZ2FnZW1lbnQgYW5kIHJldGVudGlvbiBkYXRhIHcvIGNvaG9ydCBhbmFseXNpcyBpbiBlYWNoIFpJUAogICAgLSBNYXJrZXRpbmcgYnVkZ2V0IGFuZCBtYXJrZXRpbmcgc3RyYXRlZ3kgZm9yIGVhY2ggWklQCiAgICAtIE51bWJlciBvZiBsaWNlbnNlZCBicm9rZXJzL3JlYWwgZXN0YXRlIGFnZW50cyBmb3IgZWFjaCBaSVAKCkZyb20gaGVyZSwgSSdkIGZpcnN0IGNvbXBhcmUgdGhlIG1hcmtldGluZyBidWRnZXQgYW5kIHN0cmF0ZWd5LiBEaWQgT3BlbmRvb3IgcnVuIHRoZSBzYW1lIHN0cmF0ZWd5IHdpdGggcHJvcG9ydGlvbmFsbHkgc2ltaWxhciBidWRnZXRzIC1vci0gaXMgT3BlbmRvb3IgQS9CIHRlc3RpbmcgZGlmZmVyZW50IG1hcmtldGluZyBzdHJhdGVnaWVzIHRoZXNlIG1hcmtldHM/IElmIHRoZSBtYXJrZXRpbmcgc3RyYXRlZ2llcyB3ZXJlIG1hdGVyaWFsbHkgZGlmZmVyZW50LCBJJ2QgdGhlbiBleGFtaW5lIHRoZSBpbXBhY3Qgb2YgZWFjaCBzdHJhdGVneSB0byB0aGUgZXh0ZW50IGl0IGNhdXNlcyB1bmRlci9vdmVyIHBlcmZvcm1hbmNlLiBJZiB0aGUgbWFya2V0aW5nIHN0cmF0ZWdpZXMgd2VyZSBtYXRlcmlhbGx5IHRoZSBzYW1lLCB0aGVuIEknZCB0dXJuIHRvIGludmVzdGlnYXRlIHRoZSBkZW1vZ3JhcGhpY3Mgb2YgdGhlIHR3byBaSVAgQ29kZXMgYW5kIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbWFya2V0aW5nIHN0cmF0ZWd5IGluIHRoZSB0d28gWklQIENvZGVzLiBJZiB0aGUgZGVtb2dyYXBoaWNzIGFuZCB0aGUgbWFya2V0aW5nIHdlcmUgbmVhcmx5IGlkZW50aWNhbCwgSSdkIGdldCBvbiBhIHBsYW5lIGFuZCBnbyBmaWd1cmUgb3V0IHdoYXQgaXMgZ29pbmcgb24gdmlhIGJvb3RzIG9uIHRoZSBncm91bmQuCgoKIyMjIFEzLiBGdXJ0aGVyIEV4cGxvcmF0aW9uIG9mIEh5cG90aGVzaXMgIzEKIyMjIyBQcm9tcHQKQ2hvc2Ugb25lIG9mIHlvdXIgdG9wIGh5cG90aGVzZXMgZnJvbSBRdWVzdGlvbiAyIGFuZCBhc3N1bWUgeW914oCZdmUgdmFsaWRhdGVkIHRoYXQgaXTigJlzIHRoZSBkcml2ZXIgb2YgdGhlIHBlcmZvcm1hbmNlIHRyZW5kLiBQcm9wb3NlIGEgcGxhbiBvZiBhY3Rpb24gdG8gZWl0aGVyIGNvdXJzZSBjb3JyZWN0IChpbiB0aGUgaW5zdGFuY2Ugb2YgdW5kZXJwZXJmb3JtYW5jZSkgb3IgZHJpdmUgY29udGludWVkIGdyb3d0aCAoaWYgeW914oCZdmUgaWRlbnRpZmllZCBwb3NpdGl2ZSBwZXJmb3JtYW5jZSkuCgojIyMjIEFuYWx5c2lzCioqSHlwb3RoZXNpcyAjMTogT3BlbmRvb3IgaXMgYmlkZGluZyBkaWZmZXJlbnRseSBvbiA8JDIwMEsgYW5kICQyMDBLKyBIb21lcyoqOiAoKlJlc3RhdGVkIGZyb20gUTIqKSBBbiBpbnRyaWd1aW5nIGRhdGEgcG9pbnQgaXMgdGhlIHJhdGlvIG9mICQyMDBLKyBsaXN0aW5ncyB0byA8JDIwMEsgbGlzdGluZ3MuIE9wZW5kb29yIGJlZ2lucyAyMDE3IHdpdGggYSByYXRpbyBvZiA0OjEgKDQgJDIwMEsgOiAxIDwkMjAwSykgd2hpbGUgZ2VuZXJhbCBNTFMgbGlzdGluZ3MgYXJlIGF0IDI6MS4gRHVyaW5nIFExLCBPcGVuZG9vciBhY2NlbGVyYXRlcyB0byBhIHBlYWsgb2YgODoxLCBiZWZvcmUgcHJlY2lwaXRvdXNseSBkZWNsaW5pbmcgdG8gYSByYXRpbyBvZiAxOjIgYXQgdGhlIGVuZCBvZiB0aGUgeWVhciAoYSBmYWN0b3Igb2YgMTZ4KSEgSW4gY29tcGFyaXNvbiwgTUxTIGxpc3RpbmdzIGdyb3cgZnJvbSAyOjEgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2FtcGxlIHNldCB0byBhIHBlYWsgb2YgNDoxIGF0IG1pZC15ZWFyLCBiZWZvcmUgc2V0dGxpbmcgYXQgMy4yNToxLiBBZ2FpbiwgZ2l2ZW4gdGhlIHNhbXBsZSBzaXplcyBhcmUgaGlnaGx5IHNrZXdlZCAoMTEuNToxKSwgaXQncyBub3Qgc3VycHJpc2luZyB0byBzZWUgYSBsYXJnZXIgc3dpbmcgaW4gT3BlbmRvb3IncyByYW5nZS4gSG93ZXZlciwgc3VjaCBhIHJldmVyc2FsIGxpa2VseSBpbXBsaWVzIGEgdHJlbmQgd29ydGggZXhhbWluaW5nLiBNeSBoeXBvdGhlc2lzIGlzIHRoYXQgT3BlbmRvb3Igc2VlcyBhbiBlY29ub21pYyBvcHBvcnR1bml0eSBpbiBob21lcyA8JDIwMEsuIEhvbWVzIDwkMjAwayBjb21wcmlzZSB+MzAlIG9mIHRoZSBvdmVyYWxsIG1hcmtldCAoc2ltaWxhciBhY3Jvc3MgYWxsIFpJUHMpLCBuYXR1cmFsbHkgaW1wbHlpbmcgdGhlIG1lZGlhbiBob21lIHByaWNlIGlzIGFib3ZlICQyNTBLLiBQZXJoYXBzIE9wZW5kb29yIGhhcyBkZXZlbG9wZWQgYSBzdHJhdGVneSBmb3IgaW5jcmVhc2luZyB0aGUgdmFsdWUgb2YgaG9tZXMgPCQyNTBLIG1vcmUgc3Vic3RhbnRpYWxseSB0aGFuIHRoZSBtZWRpYW4gaG9tZS4gSWYgdGhpcyBpcyB0aGUgY2FzZSwgT3BlbmRvb3IgbWF5IGJlIGZvY3VzaW5nIG9uIHRoZSBsb3cgZW5kIG9mIHRoZSBtYXJrZXQgd2hlcmUgaXQgc2VlcyBhIGdyZWF0ZXIgb3Bwb3J0dW5pdHkgZm9yIGdyb3NzIHByb2ZpdC4gCmBgYHtyfQogICAgICBwYXIobWZyb3c9YygxLDIpKQogICAgICAjIyBPcGVuZG9vcgogICAgICByYXRpb18yMDBLLm9kIDwtIG92LmRhdGEkb2RfbGlzdGluZ3MvdW4uZGF0YSRvZF9saXN0aW5ncwogICAgICBwbG90KHJhdGlvXzIwMEsub2QsbWFpbiA9ICJPRCBMaXN0OiBSYXRpbyAkMjAwSysvPCQyMDBLICIsIHhsYWIgPSAiVGltZSIsIHlsYWIgPSAiUmF0aW8iLCBjb2wgPSAiZG9kZ2VyYmx1ZTIiKQogICAgICAjIyBNTFMKICAgICAgcmF0aW9fMjAwSy5tbHMgPC0gb3YuZGF0YSRtbHNfbGlzdGluZ3MvdW4uZGF0YSRtbHNfbGlzdGluZ3MKICAgICAgcGxvdChyYXRpb18yMDBLLm1scyxtYWluID0gIk1MUyBMaXN0OiBSYXRpbyAkMjAwSysvPCQyMDBLICIsIHhsYWIgPSAiVGltZSIsIHlsYWIgPSAiUmF0aW8iLCBjb2wgPSAiZ3JleTc1IikKYGBgCgoqKkV4cGFuZGVkIFN0cmF0ZWd5OiBHcm9zcyBQcm9maXQgQXJiaXRyYWdlIDwkMjAwSyoqOiBBdCB0aGlzIHBvaW50IHdlJ3ZlIHZhbGlkYXRlZCB0aGF0IHRoZXJlIGlzIGEgZ3Jvc3MgcHJvZml0IGFyYml0cmFnZSBhdCA8JDIwMEssIHdoaWNoIGlzIHdoeSBPcGVuZG9vciBpcyBzbyBmb2N1c2VkIG9uIHB1cmNoYXNpbmcgaG9tZXMgaW4gdGhpcyBzZWdtZW50LiBIZXJlIGlzIHRoZSBzdHJhdGVneSBJJ2QgdXNlIHRvIG9wZXJhdGlvbmFsaXplIHRoaXMgcHJvZml0IG9wcG9ydHVuaXR5LgoKU3RlcCBJOiBEZWZpbmUgY2hhcmFjdGVyaXN0aWNzIG1vc3QgcHJlZGljdGl2ZSBvZiBob3VzaW5nIHByaWNlIChoeXBvdGhldGljYWwgbGlzdCBiZWxvdyk6CgogICAgLSBQcmV2aW91cyBzYWxlL3B1cmNoYXNlIHByaWNlCiAgICAtIEVzdGltYXRlZCBzdXJyb3VuZGluZyBob21lIHZhbHVlL2hpc3RvcmljYWwgcHJpY2VzCiAgICAtIFN1Yi1kaXZpZGVkIFpJUCBjb2RlcyBmb3IgbW9yZSBhY2N1cmF0ZSBLLU5lYXJlc3QgTmVpZ2hib3IgYXBwcm9hY2gvem9uaW5nCiAgICAtIEhvdXNpbmcgZGV0YWlsczogIyBvZiBiZWRzL2JhdGhzLCBzcGVjaWFsIGF0dHJpYnV0ZXMgKGd5bS9vZmZpY2UvZ2FyYWdlIHR5cGUpLCBTcSBGdCwgWWVhciBCdWlsdAogICAgLSBBY2Nlc3MgdG8gaGlnaCBzcGVlZCBpbnRlcm5ldCAvIG11bHRpcGxlIGNhYmxlIHByb3ZpZGVycwogICAgLSBQcm94aW1pdHkgdG8gYW1lbml0aWVzIChncm9jZXJ5LCByZXN0YXVyYW50cywgcGFya3MsIG5hdHVyZSkKICAgIC0gQ29tbXV0ZS9XYWxrIHNjb3JlCiAgICAKU3RlcCBJSTogQWRkIGh1bWFuIGluc3BlY3Rpb24gYnkgbG9jYWwgb3BlcmF0b3IgKFBob3RvcyB0byBhc3Nlc3MgaGVhbHRoIG9mIHBhaW50LCByb29mLCBncmFzcywgbGFuZHNjYXBlLCBldGMuKQoKU3RlcCBJSUk6IFVuZGVyc3RhbmQgaGlnaGVzdCBST0kgaG9tZSBpbXByb3ZlbWVudHMgdGhhdCBjYW4gYmUgZGVwbG95ZWQgY2hlYXBseS9xdWlja2x5IGFuZCBjcmVhdGUgaW1tZWRpYXRlIHJldHVybiAoaHlwb3RoZXRpY2FsIGxpc3QgYmVsb3cpOgoKICAgIC0gUGFpbnQKICAgIC0gQ291bnRlciB0b3BzIAogICAgLSBaZXJvIG1haW50ZW5hbmNlIGxhbmRzY2FwaW5nCiAgICAtIEFwcGxpYW5jZXMgYW5kIGxpZ2h0IGZpeHR1cmVzCiAgICAKU3RlcCBJVjogQnVpbGQgbW9kZWwgdG8gcHJlZGljdCBwcmljZSBkZWx0YSBvZiBjdXJyZW50IGNvbmRpdGlvbiBhbmQgT3BlbmRvb3IgaW1wcm92ZWQgY29uZGl0aW9uCgpTdGVwIFY6IENyZWF0ZSBhZ2dyZXNzaXZlIG1haWwvZGlnaXRhbCBjYW1wYWlnbiBjdXN0b21pemVkIHRvIGhvdXNlcyB3aXRoIGhpZ2hlc3QgcG90ZW50aWFsIHByaWNpbmcgZGVsdGEuIFVzZSBsb2NhbCBvcGVyYXRpb25hbCB0ZWFtIHRvIGhlbHAgZGlzdHJpYnV0ZSBhbmQgYnVpbGQgYXdhcmVuZXNzLiBDcmVhdGUgdW5zb2xpY2l0ZWQgb2ZmZXJzIGlmIGxlZ2FsbHkgYWxsb3dlZCBpbiBhcmVhLgoKU3RlcCBWSTogT3BlcmF0aW9uYWxpemUgYWxsIHByb2NlZHVyZXMgZm9yIGZpeGluZyBob3VzZXMuIAoKU3RlcCBWSUk6IENyZWF0ZSBiZXN0IHByYWN0aWNlcyBhcm91bmQgbGlzdGluZyBhbmQgb3BlbiBob3VzZXMuIEEvQiB0ZXN0IHdpdGggbWFya2V0aW5nIHRlYW0gdG8gYm9vc3QgY29udmVyc2lvbiBhbmQgdHJ5IHRvIGNyZWF0ZSBtdWx0aXBsZSBiaWRzIG9uIGFueSBsaXN0ZWQgaG91c2UuIAoKIyMjIEFwcGVuZGl4OiBSIENvZGUKYGBge3J9CiMjIE9QRU5ET09SIENJVFkgREFUQSBQUk9KRUNUICgzLzE4LzE5KQoKIyMgcm0obGlzdD1scygpKSAjIyBjbGVhciBXb3Jrc3BhY2UKCiMjIGRhdGEgcHJlcAogICMjIGxvYWQgZGF0YQogICAgc2V0d2QoIi9Vc2Vycy9qZWZmcmV5ZWxsaW5ndG9uL0Ryb3Bib3gvQ29kZS9PcGVuRG9vci8iKQogICAgZGF0YSA8LSByZWFkLmNzdihmaWxlID0gIkNpdHlPcHNEYXRhLmNzdiIpICMjIHRyYW5zZm9ybWF0aW9ucyBkb25lIGluIGV4Y2VsCgogICMjIGZpcnN0IGdsYW5jZSBhdCBkYXRhCiAgICBkaW0oZGF0YSkgIyMgMTggY29sdW1ucyAtLT4gYWRkZWQgMTAgY29sdW1ucyBpbiBleGNlbCAKICAgIHN0cihkYXRhKSAjIyBkYXRhIHR5cGVzIGxvYWRlZCBjb3JyZWN0bHkKICAgIHN1bW1hcnkoZGF0YSkgIyMgbm8gb2J2aW91cyBlcnJvcnMKICAgIHN1bShpcy5uYShkYXRhKSkgIyMgbm8gTi9BCgoKIyMgZGF0YSBjbGVhbmluZwogICMjIGNoZWNrIGRheSAvIG1vbnRoIC8gcXRyCiAgICB0YWJsZSh0YWJsZShkYXRhJGRhdGUpKSAjIyAzNjYgZGF5cwogICAgdGFibGUoZGF0YSRkYXRlKSAjIyAwMS8wNyBhcHBlYXJzIGluIGJvdGggMjAxNyBhbmQgMjAxOAogICAgdGFibGUoZGF0YSRtb250aCkgIyMgb2sKICAgIHRhYmxlKGRhdGEkcXRyKSAjIyBvawogIAogICMjIGNoZWNrIHppcCBjb2RlCiAgICB0YWJsZShkYXRhJHppcF9jb2RlKSAjIyBvawoKICAgIAojIyBjb2hvcnQgYW5hbHlzaXMgCiAgIyMgY3JlYXRlIDIgY29ob3J0cyAoMiBQcmljZSBMZXZlbHMpCiAgICB1bi5kYXRhIDwtIHN1YnNldChkYXRhLCBkYXRhJHByaWNlX2JhbmQgPT0gIjwkMjAwSyIpCiAgICBvdi5kYXRhIDwtIHN1YnNldChkYXRhLCBkYXRhJHByaWNlX2JhbmQgPT0gIiQyMDBLKyIpCiAgCiAgIyMgY3JlYXRlIDQgY29ob3J0cyAoNCBaaXAgQ29kZXMpCiAgICBhLmRhdGEgPC0gc3Vic2V0KGRhdGEsIGRhdGEkemlwX2NvZGUgPT0gIkEiKQogICAgYi5kYXRhIDwtIHN1YnNldChkYXRhLCBkYXRhJHppcF9jb2RlID09ICJCIikKICAgIGMuZGF0YSA8LSBzdWJzZXQoZGF0YSwgZGF0YSR6aXBfY29kZSA9PSAiQyIpCiAgICBkLmRhdGEgPC0gc3Vic2V0KGRhdGEsIGRhdGEkemlwX2NvZGUgPT0gIkQiKQogIAogICMjIGNyZWF0ZSA4IGNvaG9ydHMgKDQgWmlwQ29kZXMgKiAyIFByaWNlIExldmVscykKICAgIGEudW4uZGF0YSA8LSBzdWJzZXQoZGF0YSwgZGF0YSR6aXBfY29kZSA9PSAiQSIgJiBkYXRhJHByaWNlX2JhbmQgPT0gIjwkMjAwSyIpCiAgICBhLm92LmRhdGEgPC0gc3Vic2V0KGRhdGEsIGRhdGEkemlwX2NvZGUgPT0gIkEiICYgZGF0YSRwcmljZV9iYW5kID09ICIkMjAwSysiKQogICAgYi51bi5kYXRhIDwtIHN1YnNldChkYXRhLCBkYXRhJHppcF9jb2RlID09ICJCIiAmIGRhdGEkcHJpY2VfYmFuZCA9PSAiPCQyMDBLIikKICAgIGIub3YuZGF0YSA8LSBzdWJzZXQoZGF0YSwgZGF0YSR6aXBfY29kZSA9PSAiQiIgJiBkYXRhJHByaWNlX2JhbmQgPT0gIiQyMDBLKyIpCiAgICBjLnVuLmRhdGEgPC0gc3Vic2V0KGRhdGEsIGRhdGEkemlwX2NvZGUgPT0gIkMiICYgZGF0YSRwcmljZV9iYW5kID09ICI8JDIwMEsiKQogICAgYy5vdi5kYXRhIDwtIHN1YnNldChkYXRhLCBkYXRhJHppcF9jb2RlID09ICJDIiAmIGRhdGEkcHJpY2VfYmFuZCA9PSAiJDIwMEsrIikKICAgIGQudW4uZGF0YSA8LSBzdWJzZXQoZGF0YSwgZGF0YSR6aXBfY29kZSA9PSAiRCIgJiBkYXRhJHByaWNlX2JhbmQgPT0gIjwkMjAwSyIpCiAgICBkLm92LmRhdGEgPC0gc3Vic2V0KGRhdGEsIGRhdGEkemlwX2NvZGUgPT0gIkQiICYgZGF0YSRwcmljZV9iYW5kID09ICIkMjAwSysiKQogIAogICAgMzY2KjggIyMgZGltZW5zaW9ucyBjb3JyZWN0IAogIAogICMjIFBsb3QgdG8gc2VlIGlmIGFueSBzeXN0ZW1hdGljIGRhdGEgZXJyb3IKICAgIHBhcihtYXI9Yyg0LDQsNCw0KSkKICAgIHBhcihtZnJvdz1jKDIsMikpICMjIFBsb3RzIGZvciBNTFMgTGlzdGluZ3MgYnkgWmlwY29kZSBmb3IgPCQyMDBrCiAgICBwbG90KGEudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGIudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGMudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGQudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwYXIobWZyb3c9YygyLDIpKSAjIyBQbG90cyBmb3IgT0QgTGlzdGluZ3MgYnkgWmlwY29kZSBmb3IgPCQyMDBrCiAgICBwbG90KGEudW4uZGF0YSRvZF9saXN0aW5ncykKICAgIHBsb3QoYi51bi5kYXRhJG9kX2xpc3RpbmdzKQogICAgcGxvdChjLnVuLmRhdGEkb2RfbGlzdGluZ3MpCiAgICBwbG90KGQudW4uZGF0YSRvZF9saXN0aW5ncykKICAgIHBhcihtZnJvdz1jKDIsMikpICMjIFBsb3RzIGZvciBNTFMgTGlzdGluZ3MgYnkgWmlwY29kZSBmb3IgJDIwMGsrCiAgICBwbG90KGEub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGIub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGMub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwbG90KGQub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICBwYXIobWZyb3c9YygyLDIpKSAjIyBQbG90cyBmb3IgT0QgTGlzdGluZ3MgYnkgWmlwY29kZSBmb3IgJDIwMGsrCiAgICBwbG90KGEub3YuZGF0YSRvZF9saXN0aW5ncykKICAgIHBsb3QoYi5vdi5kYXRhJG9kX2xpc3RpbmdzKQogICAgcGxvdChjLm92LmRhdGEkb2RfbGlzdGluZ3MpCiAgICBwbG90KGQub3YuZGF0YSRvZF9saXN0aW5ncykKICAgICMjIGRhdGEgYXBwZWFycyBoaWdobHkgY29ycmVsYXRlZCBhY3Jvc3MgemlwY29kZXMgZm9yIE1MUyBhbmQgT0QgZGF0YSBpbiBlYWNoIHByaWNlIGJyYWNrZXQKCiAgICAKICAgIAogICMjIGxvYWQgZ2dwbG90MiBmb3IgZ3JhcGhpbmcKICAgICMjIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQogICAgbGlicmFyeShnZ3Bsb3QyKQogIAogICMjIEFnZ3JlZ2F0ZSBzdGF0cwogICAgIyMgMSBZZWFyIExpc3RpbmcgR3Jvd3RoCiAgICBEYXkxLk9EIDwtIHN1bShkYXRhJG9kX2xpc3RpbmdzW2RhdGEkZGF0ZSA9PSAiMjAxNy8wMS8wNyIgJiBkYXRhJHppcF9jb2RlID09IGMoIkEiLCAiQiIsICJDIiwgIkQiKV0pCiAgICBEYXkzNjYuT0QgPC0gc3VtKGRhdGEkb2RfbGlzdGluZ3NbZGF0YSRkYXRlID09ICIyMDE4LzAxLzA3IiAmIGRhdGEkemlwX2NvZGUgPT0gYygiQSIsICJCIiwgIkMiLCAiRCIpXSkKICAgIERheTEuTUxTIDwtc3VtKGRhdGEkbWxzX2xpc3RpbmdzW2RhdGEkZGF0ZSA9PSAiMjAxNy8wMS8wNyIgJiBkYXRhJHppcF9jb2RlID09IGMoIkEiLCAiQiIsICJDIiwgIkQiKV0pCiAgICBEYXkzNjYuTUxTIDwtc3VtKGRhdGEkbWxzX2xpc3RpbmdzW2RhdGEkZGF0ZSA9PSAiMjAxOC8wMS8wNyIgJiBkYXRhJHppcF9jb2RlID09IGMoIkEiLCAiQiIsICJDIiwgIkQiKV0pCiAgICBEYXkxLk9ECiAgICBEYXkzNjYuT0QKICAgIERheTEuTUxTCiAgICBEYXkzNjYuTUxTCiAgICAoRGF5MzY2Lk9EIC0gRGF5MS5PRCkvRGF5MS5PRAogICAgKERheTM2Ni5NTFMgLSBEYXkxLk1MUykvRGF5MS5NTFMKICAgICMjIDEgWWVhciBWaXNpdG9yIEdyb3d0aAogICAgRGF5MS5PRC5odiA8LSBzdW0oZGF0YSRvZF9ob21lX3Zpc2l0c1tkYXRhJGRhdGUgPT0gIjIwMTcvMDEvMDciICYgZGF0YSR6aXBfY29kZSA9PSBjKCJBIiwgIkIiLCAiQyIsICJEIildKQogICAgRGF5MzY2Lk9ELmh2IDwtIHN1bShkYXRhJG9kX2hvbWVfdmlzaXRzW2RhdGEkZGF0ZSA9PSAiMjAxOC8wMS8wNyIgJiBkYXRhJHppcF9jb2RlID09IGMoIkEiLCAiQiIsICJDIiwgIkQiKV0pCiAgICBEYXkxLk9ELmh2CiAgICBEYXkzNjYuT0QuaHYKICAgIChEYXkzNjYuT0QuaHYgLSBEYXkxLk9ELmh2KS9EYXkxLk9ELmh2CiAgICBEYXkxLk9ELmh2L0RheTEuT0QKICAgIERheTM2Ni5PRC5odi9EYXkzNjYuT0QKICAgIAogICMjIGNvbXBhcmluZyBsaXN0aW5nIGdyb3d0aCBieSBwcmljZSBiYW5kIGFuZCB6aXAgY29kZQogICAgIyMgPCQyMDBLCiAgICAgIGRhdGEuZnJhbWUodW4uZGF0YSkKICAgICAgZ2dwLnVuIDwtIGdncGxvdChkYXRhLmZyYW1lKHVuLmRhdGEpLCBhZXMoZGF0ZSkpCiAgICAjIyBwbG90dGluZyBNTFMsT0QgbGlzdGluZ3MKICAgICAgZ2dwLnVuICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1tbHNfbGlzdGluZ3MpKSsKICAgICAgICBsYWJzKHggPSAiVGltZSIsIHkgPSAiTGlzdGluZyBDb3VudCIsIHRpdGxlID0gIk1MUyBMaXN0aW5nIENvdW50OiA8JDIwMEsgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKICAgICAgZ2dwLnVuICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1vZF9saXN0aW5ncykpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJMaXN0aW5nIENvdW50IiwgdGl0bGUgPSAiT0QgTGlzdGluZyBDb3VudDogPCQyMDBLIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCgogICAgIyMgJDIwMEsrCiAgICAgIGRhdGEuZnJhbWUob3YuZGF0YSkKICAgICAgZ2dwLm92IDwtIGdncGxvdChkYXRhLmZyYW1lKG92LmRhdGEpLCBhZXMoZGF0ZSkpCiAgICAjIyBwbG90dGluZyBNTFMsT0QgbGlzdGluZ3MKICAgICAgZ2dwLm92ICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1tbHNfbGlzdGluZ3MpKSsKICAgICAgICBsYWJzKHggPSAiVGltZSIsIHkgPSAiTGlzdGluZyBDb3VudCIsIHRpdGxlID0gIk1MUyBMaXN0aW5nIENvdW50OiAkMjAwSysgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkKICAgICAgZ2dwLm92ICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1vZF9saXN0aW5ncykpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJMaXN0aW5nIENvdW50IiwgdGl0bGUgPSAiT0QgTGlzdGluZyBDb3VudDogJDIwMEsrIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCiAgICAgIAogICAgIyMgJDIwMEsrOjo8JDIwMEsgUmF0aW8KICAgICAgcGFyKG1mcm93PWMoMSwyKSkKICAgICAgIyMgT3BlbmRvb3IKICAgICAgcmF0aW9fMjAwSy5vZCA8LSBvdi5kYXRhJG9kX2xpc3RpbmdzL3VuLmRhdGEkb2RfbGlzdGluZ3MKICAgICAgcGxvdChyYXRpb18yMDBLLm9kLG1haW4gPSAiT0QgTGlzdGluZ3M6IFJhdGlvIG9mICQyMDBLKy88JDIwMEsgIiwgeGxhYiA9ICJUaW1lIiwgeWxhYiA9ICJSYXRpbyIsIGNvbCA9ICJkb2RnZXJibHVlMiIpCiAgICAgICMjIE1MUwogICAgICByYXRpb18yMDBLLm1scyA8LSBvdi5kYXRhJG1sc19saXN0aW5ncy91bi5kYXRhJG1sc19saXN0aW5ncwogICAgICBwbG90KHJhdGlvXzIwMEsubWxzLG1haW4gPSAiTUxTIExpc3RpbmdzOiBSYXRpbyBvZiAkMjAwSysvPCQyMDBLICIsIHhsYWIgPSAiVGltZSIsIHlsYWIgPSAiUmF0aW8iLCBjb2wgPSAiZ3JleTc1IikKICAgICAgICAKCiAgCiAgIyMgcGxvdHRpbmcgT0QgdG8gTUxTIGxpc3RpbmcgcmF0aW8gCiAgICAjIyBmdWxsIGRhdGEKICAgICAgZGF0YS5mcmFtZShkYXRhKQogICAgICBnZ3AgPC0gZ2dwbG90KGRhdGEuZnJhbWUoZGF0YSksIGFlcyhkYXRlLDEwMCpvZF9tbHNfbGlzdGluZ19yYXRpbykpCiAgICAgIGdncCArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwcmljZV9iYW5kKSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIlBlcmNlbnQiLCB0aXRsZSA9ICJPRDpNTFMgTGlzdGluZyBSYXRpbyAoQWxsIFppcCBDb2RlcykiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCiAgCiAgICAgIAogICMjIGNvbXBhcmluZyB2aXNpdCBkYXRhIGJ5IHByaWNlIGJhbmQgYW5kIHppcCBjb2RlCiAgICAjIyA8JDIwMEsKICAgICAgZGF0YS5mcmFtZSh1bi5kYXRhKQogICAgICBnZ3AudW4gPC0gZ2dwbG90KGRhdGEuZnJhbWUodW4uZGF0YSksIGFlcyhkYXRlKSkKICAgICMjIHBsb3R0aW5nIHZpc2l0cyBhcyBhIGZ1bmN0aW9uIG9mIGxpc3RpbmdzCiAgICAgIGdncC51biArCiAgICAgICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yPXppcF9jb2RlLHk9b2RfdmlzaXRzX2xpc3RpbmcpKSsKICAgICAgICBsYWJzKHggPSAiVGltZSIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJWaXNpdHMgUGVyIExpc3Rpbmc6IDwkMjAwSyAoWmlwIENvZGVzIEEsQixDLEQpIikrCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKQogICAgIyMgcGxvdHRpbmcgY29udHJhY3RzIGFzIGEgZnVuY3Rpb24gb2YgdmlzaXRzCiAgICAgIGdncC51biArCiAgICAgICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yPXppcF9jb2RlLHk9MTAwKm9kX2NvbnRyYWN0c192aXNpdCkpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJQZXJjZW50IiwgdGl0bGUgPSAiQ29udHJhY3RzIFBlciBWaXNpdDogPCQyMDBLIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpIAoKICAgICMjICQyMDBLKwogICAgICBkYXRhLmZyYW1lKG92LmRhdGEpCiAgICAgIGdncC5vdiA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShvdi5kYXRhKSwgYWVzKGRhdGUpKQogICAgIyMgcGxvdHRpbmcgdmlzaXRzIGFzIGEgZnVuY3Rpb24gb2YgbGlzdGluZ3MKICAgICAgZ2dwLm92ICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT1vZF92aXNpdHNfbGlzdGluZykpKwogICAgICAgIGxhYnMoeCA9ICJUaW1lIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIlZpc2l0cyBQZXIgTGlzdGluZzogJDIwMEsrIChaaXAgQ29kZXMgQSxCLEMsRCkiKSsKICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCiAgICAjIyBwbG90dGluZyBjb250cmFjdHMgYXMgYSBmdW5jdGlvbiBvZiB2aXNpdHMKICAgICAgZ2dwLm92ICsKICAgICAgICBnZW9tX2ppdHRlcihhZXMoY29sb3I9emlwX2NvZGUseT0xMDAqb2RfY29udHJhY3RzX3Zpc2l0KSkrCiAgICAgICAgbGFicyh4ID0gIlRpbWUiLCB5ID0gIlBlcmNlbnQiLCB0aXRsZSA9ICJDb250cmFjdHMgUGVyIFZpc2l0OiAkMjAwSysgKFppcCBDb2RlcyBBLEIsQyxEKSIpKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkgCgogICMjIGV2YWx1YXRpbmcgY29udHJhY3RzIHBlciBsaXN0aW5nIGJ5IGNvaG9ydAogICAgIyMgT0QgYXZlcmFnZSBieSBjb2hvcnQKICAgICAgYS51Lm9kIDwtIG1lZGlhbihhLnVuLmRhdGEkb2RfY29udHJhY3RzL2EudW4uZGF0YSRvZF9saXN0aW5ncykKICAgICAgYS5vLm9kIDwtIG1lZGlhbihhLm92LmRhdGEkb2RfY29udHJhY3RzL2Eub3YuZGF0YSRvZF9saXN0aW5ncykKICAgICAgYi51Lm9kIDwtIG1lZGlhbihiLnVuLmRhdGEkb2RfY29udHJhY3RzL2IudW4uZGF0YSRvZF9saXN0aW5ncykKICAgICAgYi5vLm9kIDwtIG1lZGlhbihiLm92LmRhdGEkb2RfY29udHJhY3RzL2Iub3YuZGF0YSRvZF9saXN0aW5ncykKICAgICAgYy51Lm9kIDwtIG1lZGlhbihjLnVuLmRhdGEkb2RfY29udHJhY3RzL2MudW4uZGF0YSRvZF9saXN0aW5ncykKICAgICAgYy5vLm9kIDwtIG1lZGlhbihjLm92LmRhdGEkb2RfY29udHJhY3RzL2Mub3YuZGF0YSRvZF9saXN0aW5ncykKICAgICAgZC51Lm9kIDwtIG1lZGlhbihkLnVuLmRhdGEkb2RfY29udHJhY3RzL2QudW4uZGF0YSRvZF9saXN0aW5ncykKICAgICAgZC5vLm9kIDwtIG1lZGlhbihkLm92LmRhdGEkb2RfY29udHJhY3RzL2Qub3YuZGF0YSRvZF9saXN0aW5ncykKICAgICMjIE9EIGF2ZXJhZ2UgYnkgY29ob3J0CiAgICAgIGEudS5tbHMgPC0gbWVkaWFuKGEudW4uZGF0YSRtbHNfY29udHJhY3RzL2EudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGEuby5tbHMgPC0gbWVkaWFuKGEub3YuZGF0YSRtbHNfY29udHJhY3RzL2Eub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGIudS5tbHMgPC0gbWVkaWFuKGIudW4uZGF0YSRtbHNfY29udHJhY3RzL2IudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGIuby5tbHMgPC0gbWVkaWFuKGIub3YuZGF0YSRtbHNfY29udHJhY3RzL2Iub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGMudS5tbHMgPC0gbWVkaWFuKGMudW4uZGF0YSRtbHNfY29udHJhY3RzL2MudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGMuby5tbHMgPC0gbWVkaWFuKGMub3YuZGF0YSRtbHNfY29udHJhY3RzL2Mub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGQudS5tbHMgPC0gbWVkaWFuKGQudW4uZGF0YSRtbHNfY29udHJhY3RzL2QudW4uZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAgIGQuby5tbHMgPC0gbWVkaWFuKGQub3YuZGF0YSRtbHNfY29udHJhY3RzL2Qub3YuZGF0YSRtbHNfbGlzdGluZ3MpCiAgICAjIyBjcmVhdGUgbGlzdHMgYW5kIGRhdGFmcmFtZXMKICAgICAgb2QubWVkaWFucyA8LSAxMDAqYyhhLnUub2QsIGEuby5vZCwgYi51Lm9kLCBiLm8ub2QsIGMudS5vZCwgYy5vLm9kLCBkLnUub2QsIGQuby5vZCkKICAgICAgbWxzLm1lZGlhbnMgPC0gMTAwKmMoYS51Lm1scywgYS5vLm1scywgYi51Lm1scywgYi5vLm1scywgYy51Lm1scywgYy5vLm1scywgZC51Lm1scywgZC5vLm1scykKICAgICAgZGlmZi5tZWRpYW5zIDwtIChvZC5tZWRpYW5zLW1scy5tZWRpYW5zKQogICAgICBjb2hvcnQubmFtZXMgPC0gYygiQSA8JDIwMEsiLCAiQSAkMjAwSysiLCJCIDwkMjAwSyIsICJCICQyMDBLKyIsIkMgPCQyMDBLIiwgIkMgJDIwMEsrIiwiRCA8JDIwMEsiLCAiRCAkMjAwSysiKQogICAgICBjb2hvcnQuZGYgPC0gZGF0YS5mcmFtZShDb2hvcnQgPSBjb2hvcnQubmFtZXMsIE9EbWVkaWFuID0gb2QubWVkaWFucywgTUxTbWVkaWFuID0gbWxzLm1lZGlhbnMsIERpZmY9ZGlmZi5tZWRpYW5zKQogICAgICBjb2hvcnQuZGYKICAgICMjIGFkZGluZyB0aW1lIHNlcmllcyBpbmZvcm1hdGlvbiBieSBxdWFydGVyCiAgICAgIHF0ci5kYXRhIDwtIHJlYWQuY3N2KGZpbGUgPSAiQ2l0eUNvaG9ydHMuY3N2IikgIyMgcmVhZCBpbiBQaXZvdCBUYWJsZSBmcm9tIGV4Y2VsLCBpZ25vcmUgZXJyb3IgbWVzc2FnZQogICAgICBxdHIuZGF0YSAKICAgICAgIyMgMzIgZGF0YSBwb2ludHM6IDQgUXVhcnRlcnMgKiAyIFByaWNlIExldmVscyAqIDQgWmlwIENvZGVzCiAgICAgICMjIFFUUiBkYXRhIGlzIHJlc3VsdCBvZiBPRCBtZWRpYW4gY29udHJhY3Qgd2luIHJhdGUvTUxTIG1lZGlhbiBjb250cmFjdCB3aW4gcmF0ZSAoc28sIGl0IHJlZmxlY3RzIHRoZSBkaWZmZXJlbmNlIGluICUgcG9pbnRzKQogICAgICBxdHIuZGF0YSRRVFIgPC0gYygxOjQpCiAgICAgIHF0ci5kYXRhJFFUUiA8LSBhcy5udW1lcmljKHF0ci5kYXRhJFFUUikKICAgICAgZGF0YS5mcmFtZShxdHIuZGF0YSkKICAgICAgc3RyKHF0ci5kYXRhKQogICAgICBnZ3AuY29ob3J0cyA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShxdHIuZGF0YSksIGFlcyh4PVFUUikpCiAgICAgICMjIDwkMjAwSyBzdWJzZWN0aW9uCiAgICAgICAgZ2dwLmNvaG9ydHMgKyAKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkEuLjIwMEspLCBjb2xvciA9ICIjRjg3NjZEIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQS4uMjAwSyksIGNvbG9yID0gIiNGODc2NkQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkIuLjIwMEspLCBjb2xvciA9ICIjN0NBRTAwIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQi4uMjAwSyksIGNvbG9yID0gIiM3Q0FFMDAiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkMuLjIwMEspLCBjb2xvciA9ICIjMDBDRkI0IikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqQy4uMjAwSyksIGNvbG9yID0gIiMwMENGQjQiKSsKICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHk9MTAwKkQuLjIwMEspLCBjb2xvciA9ICIjQzc3Q0ZGIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0xMDAqRC4uMjAwSyksIGNvbG9yID0gIiNDNzdDRkYiKSsKICAgICAgICAgICAgZ2VvbV9saW5lKGFlcyh5PTApLCBjb2xvcj0iQmxhY2siKSsKICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikrCiAgICAgICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiRGlmZmVyZW5jZTogUGVyY2VudGFnZSBQb2ludHMiLCB0aXRsZSA9ICJEaWZmZXJlbmNlIGluIE9EOk1MUyBDb250cmFjdC9MaXN0aW5nIFdpbiBSYXRlOiA8JDIwMEssIEJ5IFF1YXJ0ZXIiKQogICAgICAjIyAkMjAwSysgc3Vic2VjdGlvbgogICAgICAgIGdncC5jb2hvcnRzICsgCiAgICAgICAgICBnZW9tX3BvaW50KGFlcyh5PTEwMCpBLjIwMEsuKSwgY29sb3IgPSAiI0Y4NzY2RCIpKwogICAgICAgICAgICBnZW9tX2xpbmUoYWVzKHk9MTAwKkEuMjAwSy4pLCBjb2xvciA9ICIjRjg3NjZEIikrCiAgICAgICAgICBnZW9tX3BvaW50KGFlcyh5PTEwMCpCLjIwMEsuKSwgY29sb3IgPSAiIzdDQUUwMCIpKwogICAgICAgICAgICBnZW9tX2xpbmUoYWVzKHk9MTAwKkIuMjAwSy4pLCBjb2xvciA9ICIjN0NBRTAwIikrCiAgICAgICAgICBnZW9tX3BvaW50KGFlcyh5PTEwMCpDLjIwMEsuKSwgY29sb3IgPSAiIzAwQ0ZCNCIpKwogICAgICAgICAgICBnZW9tX2xpbmUoYWVzKHk9MTAwKkMuMjAwSy4pLCBjb2xvciA9ICIjMDBDRkI0IikrCiAgICAgICAgICBnZW9tX3BvaW50KGFlcyh5PTEwMCpELjIwMEsuKSwgY29sb3IgPSAiI0M3N0NGRiIpKwogICAgICAgICAgICBnZW9tX2xpbmUoYWVzKHk9MTAwKkQuMjAwSy4pLCBjb2xvciA9ICIjQzc3Q0ZGIikrCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT0wKSwgY29sb3IgPSAiQmxhY2siKSsKICAgICAgICAgIGxhYnMoeCA9ICJRdWFydGVyIiwgeSA9ICJEaWZmZXJlbmNlOiBQZXJjZW50YWdlIFBvaW50cyIsIHRpdGxlID0gIkRpZmZlcmVuY2UgaW4gT0Q6TUxTIENvbnRyYWN0L0xpc3RpbmcgV2luIFJhdGU6ICQyMDBLKywgQnkgUXVhcnRlciIpCmBgYAo=