• Introduction
  • Prerequisites
  • Dataset
    • Rating Classification
    • Create a Reviewer Profile
      • Split Taster Data
      • Create Reviewer Metrics
    • Normalized Points
    • Data Sanitation
  • Data Exploration
    • Univariate Exploration
      • Alcohol Amount
      • Vintage
      • Winery
      • Province
      • Wine Category
      • Price
      • Points
    • Multivariate Exploration
      • Points by Taster
      • Price by Points
  • Data Analysis
    • What is the best wine?
    • What is the best value wine?
    • Which province has the best wine?
    • Which wine variety is the best?
    • Jess from New Girl’s favorite wine?
  • Conclusion
  • Future Works
  • Datasets Cited

Team members

Saivamsi Hanumanthu
Vamso the Extraordinaire

Osaki Pokima
Saki-Saki

Isabel Zimmerman
Z Best

Introduction

Turning 21 is a milestone in everyone’s life and usually, people celebrate it with a family dinner and glass of wine. But, how does one find the best wine considering that person has never had alcohol.

  • Do they get the best rated/most expensive bottle? (Too bad college students are poor)

  • Do they try to find the best value wine? (Ballin’ on a budget)

  • Or do they follow Jess from New Girl? (Obviously, the only option)

Let’s take a deep dive and answer these pressing questions!

Our project investigates ~120,000 wine reviews with characteristics such as variety, vintage, winery, price, etc. This is our Final Project for Intro to Data Science (Spring 2020) class, and we thought it was a bit too easy, so we decided to expand it and take it a step further.

Prerequisites

Loading the required packages

library(tidyverse)
library(dplyr)
library(ggplot2)
library(scales)

Dataset

Import processed data, which can be found here. Note: This file also credits our dataset curators and outlines our data cleaning pipeline.

#read preprocessed data
wines <- read.csv(file = '../data/processed_data/wines.csv')

Get a sample of the dataset for easy testing. However, for our final report, we ran all of our code on the full wines dataset to get more accurate results.

#set seed value to birthday of Ricardo Rodriguez, American wrestler and ring announcer and Dr. Reinaldo (Rei) Sanchez-Arias
set.seed(19630217)

#set percentage to test with for simplicity, if needed
percentage <- 5
wine_sample<- sample_n(wines, percentage/100*nrow(wines))

Rating Classification

Wines are normally classified in categories as found on this website. To create a more rich dataset we added the field rating_category determined as:

Category Rating Description
Classic 98-100 The pinnacle of quality.
Superb 94-97 A great achievement.
Excellent 90-93 Highly recommended.
Very Good 87-89 Often good value; well recommended.
Good 83-86 Suitable for everyday consumption; often good value.
Acceptable 80-82 Can be employed in casual, less-critical circumstances

This allows to take a quantitative data and turn it into qualitative data, which can be used for data visualization later.

# function to add rating
rating_category <- function(points){
  if(points>=98){
    return("Classic")
  }
  else if (points>=94){
    return("Superb")
  }
  else if(points>=90){
    return("Excellent")
  }
  else if(points>=87){
    return("Very Good")
  }
  else if(points>=83){
    return("Good")
  }
  else{
    return("Acceptable")
  }
}

wines<- wines %>%
  rowwise() %>%
  mutate(rating_category = rating_category(points))
head(wines)
ABCDEFGHIJ0123456789
title
<chr>
Nicosia 2013 Vulkà Bianco (Etna)
Quinta dos Avidagos 2011 Avidagos Red (Douro)
Rainstorm 2013 Pinot Gris (Willamette Valley)
St. Julian 2013 Reserve Late Harvest Riesling (Lake Michigan Shore)
Sweet Cheeks 2012 Vintner's Reserve Wild Child Block Pinot Noir (Willamette Valley)
Tandem 2011 Ars In Vitro Tempranillo-Merlot (Navarra)

Create a Reviewer Profile

Each reviewer has there own bias. In order to offset that we made a “profile” for each reviewer. This profile allows us to later normalize the wine points for more robust apples to apples comparison.

Split Taster Data

To make our dataframes more managable we split reduntant information about the tasters into a new dataframe.

tasters <- wines %>%
  select(taster_name, taster_twitter_handle) %>% 
  unique()
tasters
ABCDEFGHIJ0123456789
taster_name
<chr>
taster_twitter_handle
<chr>
Kerin O’Keefe@kerinokeefe
Roger Voss@vossroger
Paul Gregutt@paulgwine
Alexander Peartree
Michael Schachner@wineschach
Anna Lee C. Iijima
Virginie Boone@vboone
Matt Kettmann@mattkettmann
Sean P. Sullivan@wawinereport

Drop taster_twitter_handle in wines dataframe to reduce reduntant information.

wines <- wines %>%
  select(-taster_twitter_handle)
head(wines)
ABCDEFGHIJ0123456789
title
<chr>
Nicosia 2013 Vulkà Bianco (Etna)
Quinta dos Avidagos 2011 Avidagos Red (Douro)
Rainstorm 2013 Pinot Gris (Willamette Valley)
St. Julian 2013 Reserve Late Harvest Riesling (Lake Michigan Shore)
Sweet Cheeks 2012 Vintner's Reserve Wild Child Block Pinot Noir (Willamette Valley)
Tandem 2011 Ars In Vitro Tempranillo-Merlot (Navarra)

Create Reviewer Metrics

Each reviewers profile includes the following metrics to create a profile:

  • avg_points which is the average of all the reviewer’s scores

  • sd_points which is the standard deviation of all the reviewer’s scores

  • var_points which is the variance of all the reviewer’s scores

  • reviews which is the number of reviews conducted

taster_rating_profile <- wines %>%
  group_by(taster_name) %>%
  summarize(
    avg_points = mean(points),
    sd_points = sd(points),
    var_points = var(points),
    reviews = n()
  )
`summarise()` ungrouping output (override with `.groups` argument)
tasters <- inner_join(tasters, taster_rating_profile, by = "taster_name")
head(tasters)
ABCDEFGHIJ0123456789
taster_name
<chr>
taster_twitter_handle
<chr>
avg_points
<dbl>
sd_points
<dbl>
var_points
<dbl>
reviews
<int>
Kerin O’Keefe@kerinokeefe88.864862.4945546.2228029990
Roger Voss@vossroger88.725583.0421849.25488423781
Paul Gregutt@paulgwine89.066592.8235017.9721598845
Alexander Peartree85.838961.9472313.791707385
Michael Schachner@wineschach86.879493.0281269.16954914430
Anna Lee C. Iijima88.423602.5747546.6293614254

Normalized Points

As mentioned, each reviewer has a different bias. To offset this, we created a normalized metric, norm_points, by looking at the number of standard deviations a wine is from the reviewer’s avg_points. This gives us a more accurate representation of which wines are “better” than the rest.

normalize_points <- function(data){
  left_join(data, tasters, by = "taster_name")%>%
    rowwise() %>%
    mutate(norm_points = (points-avg_points)/sd_points) %>%
    select(-avg_points, -sd_points, -var_points, -taster_twitter_handle, -reviews)
}

wines <- normalize_points(wines)
head(wines) 
ABCDEFGHIJ0123456789
title
<chr>
Nicosia 2013 Vulkà Bianco (Etna)
Quinta dos Avidagos 2011 Avidagos Red (Douro)
Rainstorm 2013 Pinot Gris (Willamette Valley)
St. Julian 2013 Reserve Late Harvest Riesling (Lake Michigan Shore)
Sweet Cheeks 2012 Vintner's Reserve Wild Child Block Pinot Noir (Willamette Valley)
Tandem 2011 Ars In Vitro Tempranillo-Merlot (Navarra)

Data Sanitation

To ensure the integrity of our data, we perform some hard checks that could have been missed during our data pre-processing.

Vintage seems to have year 7200, so we filtered all data up to 2019

wines <- wines %>%
  filter(vintage<2019)

Data Exploration

Before, conducting any detailed analysis of our dataset, we looked at a quick summary of the dataset

summary(wines)
    title              alcohol          category            vintage     designation          country            province        
 Length:117878      Min.   :   2.20   Length:117878      Min.   :1150   Length:117878      Length:117878      Length:117878     
 Class :character   1st Qu.:  13.00   Class :character   1st Qu.:2009   Class :character   Class :character   Class :character  
 Mode  :character   Median :  13.53   Mode  :character   Median :2011   Mode  :character   Mode  :character   Mode  :character  
                    Mean   :  13.84                      Mean   :2010                                                           
                    3rd Qu.:  14.40                      3rd Qu.:2013                                                           
                    Max.   :8333.00                      Max.   :2017                                                           
                    NA's   :9060                                                                                                
    region           subregion           variety             winery              price             points       taster_name       
 Length:117878      Length:117878      Length:117878      Length:117878      Min.   :   4.00   Min.   : 80.00   Length:117878     
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  17.00   1st Qu.: 86.00   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  25.00   Median : 88.00   Mode  :character  
                                                                             Mean   :  35.48   Mean   : 88.47                     
                                                                             3rd Qu.:  42.00   3rd Qu.: 91.00                     
                                                                             Max.   :3300.00   Max.   :100.00                     
                                                                             NA's   :7974                                         
 rating_category     norm_points      
 Length:117878      Min.   :-4.49580  
 Class :character   1st Qu.:-0.72711  
 Mode  :character   Median : 0.03980  
                    Mean   : 0.01183  
                    3rd Qu.: 0.70027  
                    Max.   : 4.46378  
                                      

Univariate Exploration

To better understand the distribution of our data, we did some simple univariate visualization based on specific fields. Additionally, before doing a multivariate analysis and answering our research questions, we first want to ensure our dataset is robust and an accurate representation of the real world.

Alcohol Amount

The visualization below depicts the distribution of our dataset based on alcohol percentage, alcohol. To better understand and visualize the data, we categorized the graph based on rating_category. Notice, a majority of wines have an alcohol amount between 12%-15%, and according to Real Simple, wine alcoholic content averages between 11%-13%. This leads us to believe our data is an accurate representation of the real world.

wines %>% 
  group_by(alcohol) %>% 
  ggplot() +
  geom_histogram(
    mapping = aes(
      x = alcohol, 
      fill = rating_category),
    na.rm = TRUE,
    bins = 50) +
  scale_x_continuous(
    breaks = seq(0,25,1), 
    limits = c(4,22)) +
  labs(
    title = "Distribution of Alcohol Percentage",
    x = "Alcohol Percentage",
    y = "Count",
    fill = "Rating Category"
  )

Vintage

Next, we wanted to see what vintage most of the wines in the dataset were. Again to better understand and visualize the data, we categorized the graph based on rating_category. Notice that there is roughly an equal percentage of each rating category per vintage.

(Note: Data points before 1990 have been omitted for clarity in visualization)

wines %>%
  ggplot() +
  geom_bar(
    mapping = aes(
      x=vintage, 
      fill = rating_category),
    na.rm = TRUE) +
  scale_x_continuous(
    breaks = seq(1990,2019,5), 
    limits = c(1990,2019)) +
  labs(
    title = "Distribution of Vintage",
    x = "Vintage", 
    y = "Count",
    fill = "Rating Category")

Winery

To better understand the number wines per winery, we did a visualization that counts the number of wines per winery showing only Top 10 wineries to give you an idea of what winery has the most selection of wines. Notice, each of the top 10 producers of wine have over 100 different wine labels.

wines %>%
  group_by(winery) %>%
  summarise(count = n()) %>%
  arrange(desc(count)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x= reorder(winery, count),
      y = count,
      fill = winery)) +
  labs(
    title = "Distribution of Winery (Top 10)",
    x = "Winery", 
    y = "Count") +
  theme(legend.position = "none") +
  coord_flip()
`summarise()` ungrouping output (override with `.groups` argument)

Province

To better understand the number of wines per province, we did a visualization that counts the number of wines per province, showing only the top 10 provinces with the most wines. This can give the reader an idea where their wine will most likely be made with California standing out as a clear leader.

wines %>% 
  group_by(province) %>% 
  summarize(count = n()) %>% 
  arrange(desc(count)) %>% 
  slice(1:10) %>% 
  ggplot()+
  geom_col(
    mapping = aes(
      x = reorder(province, count), 
      y = count,
      fill = province)) +
  labs(
    title = "Distribution of Province (Top 10)",
    x = "Province", 
    y = "Count") +
  theme(legend.position = "none") +
  coord_flip()
`summarise()` ungrouping output (override with `.groups` argument)

Wine Category

Next, we wanted to visualize the distribution of different wine categories in our dataset. To better understand and visualize the data, we categorized the graph based on rating_category. Notice, a majority of the wines are red or white wines.

wines %>% 
  ggplot()+
  geom_bar(
    mapping = aes(
      x = category,
      fill = rating_category)) +
  labs(
    title = "Distribution of Wine Category",
    x = "Wine Category", 
    y = "Count",
    fill = "Rating Category") +
  coord_flip()

Price

Next, we wanted to visualize the distribution of price in our dataset. To better understand and visualize the data, we categorized the graph based on rating_category. Notice, a majority of wines are $50 and below, with the most common being between $12 - $25. Again, this accurately represents the real world. As stated by Vivino, the average price for a good bottle of red/white wine is ~$15 and ~$28 for a very good bottle. (CAUTION: The Vivino prices denoted were simply an average for red/white wines average costs. This was done to generalize the information to make a simple comparison. Also, this limited to red/white wine and does not accurately include other types, which are included within our dataset)

(Note: Data points above $400 have been omitted for clarity in visualization)

wines %>% 
  filter(price < 400) %>% 
  ggplot() +
  geom_histogram(
    mapping = aes(
      x=price, 
      fill = rating_category),
    binwidth = 15) +
  labs(
    title = "Distribution of Price",
    x = "Price", 
    y = "Count",
    fill = "Rating Category")

Points

Next, we wanted to visualize the distribution of points in our dataset. Notice, here that a majority of wines receive a score between 87 and 90. Which is accurate to the information provided on Wine Searcher, which states 50% of the scores fall between 86-90 point from Wine Enthusiast ratings.

(Note: We our dataset was reterived from the Wine Enthusiast website)

wines %>%
  ggplot() +
  geom_histogram(
    mapping = aes(x=points),
    bins = 20)+
  labs(
    title = "Distribution of Points",
    x = "Points", 
    y = "Count")

Multivariate Exploration

Now that we have a better understanding of our data, and we know it is an accurate representation of the real world, we can perform a more detailed analysis using multiple variables.

Points by Taster

To understand the point distribution by tasters, we did a multivariate visualization that correlates taster names based on the average wine points as identified by the x-intercept. This gives the reader an idea of how some reviewers correlate to the overall average.

(Note: The “blank” represents unknown reviewers. We assumed the reviewers not named have not rated a significant amount of wines and can be grouped into a singular reviewer)

wines %>%
  ggplot() +
  geom_boxplot(
    mapping = aes(
      x=taster_name,
      y=points, 
      color = taster_name)) +
  geom_hline(yintercept = mean(wines$points)) +
  theme(legend.position = "none")+
  labs(
    title = "Points by Taster",
    x="Taster Name",
    y="Points"
  )+
  coord_flip()

Price by Points

To understand the price distribution by points, we did a multivariate visualization that creates a scatter plot of the wines based on points and price. Then, we added a smooth transformation to identify trends. Notice, the data is stacked, and the scores range from 80-100

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = price, 
      color = category),
    na.rm = TRUE,
    alpha = 0.2) +
  labs(
    title = "Price by Points", 
    x = "Points",
    y = "Price",
    color = "Wine Category") +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = price),
    na.rm = TRUE)

Since there are multiple outliers, and the visualization is clustered. By performing a log on all the prices, we can reduce the skewness of the visualization. Notice, as the quality of wine increases, price increases exponentially.

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = log(price), 
      color = category),
    na.rm = TRUE,
    alpha = 0.2) +
  labs(
    title = "log(Price) by Points", 
    x = "Points",
    y = "log(Price)",
    color = "Wine Category") +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = log(price)),
    na.rm = TRUE)

Group by Wine Category

Next, our group wanted to do a more granular analysis by looking at how the price varies by points grouped by the wine category. Notice, all the prices go up as points go up, but the growth rates are different per wine category.

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = log(price), 
      color = category),
    alpha =0.2,
    na.rm = TRUE) +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = log(price)),
    na.rm = TRUE) +
  facet_wrap(~category) +
  labs(
    title = "log(Price) by Points", 
    x = "Points",
    y = "log(Price)")+
  theme_minimal()+
  theme(legend.position = "none" )

Data Analysis

Now that we fully understand the dataset we are working with, we plan to answer some research questions proposed by the team.

What is the best wine?

An easy way to determine the best wine is by simply finding the top 10 wines by rating.

wines %>%
  arrange(desc(points)) %>%
  slice(1:10) %>%
  select(title,price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
Avignonesi 1995 Occhio di Pernice (Vin Santo di Montepulciano)
Krug 2002 Brut (Champagne)
Tenuta dell'Ornellaia 2007 Masseto Merlot (Toscana)
Casa Ferreirinha 2008 Barca-Velha Red (Douro)
Biondi Santi 2010 Riserva (Brunello di Montalcino)
Cardinale 2006 Cabernet Sauvignon (Napa Valley)
Château Léoville Barton 2010 Saint-Julien
Louis Roederer 2008 Cristal Vintage Brut (Champagne)
Salon 2006 Le Mesnil Blanc de Blancs Brut Chardonnay (Champagne)
Château Lafite Rothschild 2010 Pauillac

However, this does not account for the taster’s bias. Instead, our group normalized the points based on each taster based on the number of standard deviations a wine is from the raters average. For example, Taster A could give a wine 100 but has an average rating score of 95 with a standard deviation of 5. Whereas, Taster B could offer a wine 91 and have an average score of 87 with a standard deviation of 2. Although the wine tasted by Taster A got a perfect 100 score, Taster B’s wine was much “better” wine since it was two standard deviations from the tasters average compared to 1 standard deviation of the other wine.

Looking at the norm_points these are the top 10 best wines

wines %>%
  arrange(desc(norm_points)) %>%
  slice(1:10)%>%
  select(title,price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
Biondi Santi 2010 Riserva (Brunello di Montalcino)
Tenuta San Guido 2012 Sassicaia (Bolgheri Sassicaia)
Mascarello Giuseppe e Figlio 2008 Cà d'Morissio Riserva (Barolo)
Mascarello Giuseppe e Figlio 2010 Monprivato (Barolo)
Il Marroneto 2012 Madonna delle Grazie (Brunello di Montalcino)
Charles Smith 2006 Royal City Syrah (Columbia Valley (WA))
Cayuse 2008 Bionic Frog Syrah (Walla Walla Valley (WA))
Oremus 2005 Eszencia (Tokaji)
Royal Tokaji 2013 5 Puttonyos Aszú Red Label (Tokaji)
Dobogó 2007 Aszú 6 Puttonyos (Tokaji)

What is the best value wine?

A simple value metric we can use to determine best value is points/price.

wines %>%
  arrange(desc(points/price)) %>%
  slice(1:10)%>%
  select(title,price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
price
<int>
Cramele Recas 2011 UnWineD Pinot Grigio (Viile Timisului)4
Felix Solis 2013 Flirty Bird Syrah (Vino de la Tierra de Castilla)4
Dancing Coyote 2015 White (Clarksburg)4
Broke Ass 2009 Red Malbec-Syrah (Mendoza)4
Terrenal 2010 Cabernet Sauvignon (Yecla)4
Terrenal 2010 Estate Bottled Tempranillo (Yecla)4
Felix Solis 2012 Flirty Bird White (Vino de la Tierra de Castilla)4
In Situ 2008 Reserva Sauvignon Blanc (Aconcagua Valley)5
In Situ 2008 Reserva Sauvignon Blanc (Aconcagua Valley)5
Earth's Harvest 2013 Merlot (California)5

However, again this metric is not normalized. Instead, norm_points/price would yield more robust results.

wines %>%
  arrange(desc(norm_points/price)) %>%
  slice(1:10) %>%
  select(title,price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
Lyrarakis 2014 Kotsifali (Crete)
Osborne NV Pedro Ximenez 1827 Sweet Sherry Sherry (Jerez)
Dionysos 2014 Moschofilero (Mantinia)
Chateau Grand Traverse 2013 Dry Riesling (Old Mission Peninsula)
Hatzimichalis 2015 Assyrtiko (Atalanti Valley)
Royal Tokaji 2016 Late Harvest (Tokaji)
Quinta dos Murças 2011 Assobio Red (Douro)
Boutari 2016 Moschofilero (Mantinia)
Lovingston 2012 Josie's Knoll Merlot (Monticello)
Rulo 2007 Syrah (Columbia Valley (WA))

Which province has the best wine?

To determine the best province for wine by points, we averaged the points of all wines per province with a sample size greater than 30 and returned the top 10 with standard error. Notice how the standard error is low, meaning the spread of our data is also small, and the average is very accurate.

wines %>% 
  group_by(province) %>%
  summarise(
    avg_points_prov = mean(points), 
    count = n(), 
    std_err_points_prov = sd(points)/sqrt(count)) %>%
  filter(count>30) %>%
  arrange(desc(avg_points_prov)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x = reorder(province,avg_points_prov), 
      y = avg_points_prov,
      fill = province)) +
   geom_errorbar(
    mapping = aes(
      x = province,
      y = avg_points_prov,
      ymin = avg_points_prov - std_err_points_prov, 
      ymax = avg_points_prov + std_err_points_prov
      ),
    width = 0.2)+
  scale_y_continuous(
    limits=c(85,95), 
    oob = rescale_none)+
  labs(
      x = 'Province', 
      y = "Average Points", 
      title = "Average Points By Province (Top 10)") +
  theme(legend.position = "none")+
  coord_flip()
`summarise()` ungrouping output (override with `.groups` argument)

Which wine variety is the best?

To determine the best variety of wine, we use the average points of all wines per variety with a sample size greater than 30. The graph below shows the top 10 varieties with their respective standard error.

wines %>% 
  group_by(variety) %>%
  summarise(
    avg_points_variety = mean(points), 
    count = n(), 
    std_err_points_variety = sd(points)/sqrt(count)) %>%
  filter(count>30) %>%
  arrange(desc(avg_points_variety)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x = reorder(variety,avg_points_variety), 
      y = avg_points_variety,
      fill = variety)) +
   geom_errorbar(
    mapping = aes(
      x = variety,
      y = avg_points_variety,
      ymin = avg_points_variety - std_err_points_variety, 
      ymax = avg_points_variety + std_err_points_variety
      ),
    width = 0.2)+
  scale_y_continuous(
    limits=c(85,95), 
    oob = rescale_none)+
  labs(
      x = 'Variety', 
      y = "Average Points", 
      title = "Average Points By Variety (Top 10)") +
  theme(legend.position = "none")+
  coord_flip()
`summarise()` ungrouping output (override with `.groups` argument)

Jess from New Girl’s favorite wine?

Sounds like a silly question, but take a closer look and you’ll find an interesting question within it: “Can we determine the best value wine based on how much people are willing to pay?” WE SURE CAN!

# If you want to get user input
#user_price <- readline(prompt = "How much are you willing to spend on a bottle?")
#user_price <- as.integer(user_price)
user_price<-11

wines %>% 
  filter(price <= user_price) %>%
  arrange(desc(norm_points/price)) %>%
  slice(1:10) %>%
  select(title, price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
Mano A Mano 2011 Tempranillo (Vino de la Tierra de Castilla)
Herdade dos Machados 2012 Toutalga Red (Alentejano)
Dolce Stefania 2007 Bonarda (Mendoza)
Viña Cumbrero 2010 Crianza (Rioja)
Borsao 2008 Monte Oton Garnacha (Campo de Borja)
Aveleda 2011 Follies Fonte Nossa Senhora da Vandoma Touriga Nacional-Cabernet Sauvignon (Bairrada)
Pedra Cancela 2010 Seleção do Enólogo Red (Dão)
Pacific Rim 2009 Riesling (Columbia Valley (WA))
Trivento 2009 Reserve Cabernet Sauvignon (Mendoza)
Chakana 2006 Malbec (Mendoza)
Now back to the orignal question with Jess
wines %>% 
  filter(price < 11) %>%
  filter(category == "Sparkling") %>%
  filter(grepl("pink", title, ignore.case = T) == T) %>%
  select(title,price, points,rating_category, norm_points)
ABCDEFGHIJ0123456789
title
<chr>
price
<int>
points
<int>
Yellow Tail 2015 Pink Bubbles Sparkling (South Australia)1084

As you can see, “Yellow Tail 2015 Pink Bubbles Sparkling (South Australia)” is Jess’s type of wine!

Conclusion

After all this exploration, we were able to walk away with some insights. We learned that standardizing tasters gives a more accurate overview of what the best wines really are, that point values affect price, and that you’re most likely drinking wine from California. In the end, though, this exploration of wine showed us more than just how to design the perfect flight; we learned the importance of data preprocessing, the power of mindful graphics, how to make every ggplot easy to understand for the reader using color, and the practicality of the pipe tool. We were able to apply data manipulation and exploration skills such as filtering, mutating, arranging, greping, and slicing data. Beyond R skills, we learned how to adapt to being a remote team, which was significantly helped with the utilization of GitHub. We feel this report gives a well-rounded display of the tools we were taught throughout the semester, and we were excited to build from that knowledge and integrate other tools as well.

Future Works

There is undoubtedly more exploration to be done within this dataset. Integrating more Twitter data with packages such as rtweet, we could design the “perfect stereotype” of a wine connoisseur by gathering keywords from tweets. Delving into the text mining portion of data science, we could find popular words to describe a wine from each review and design artificial reviews. We could take the data we found, build statistical learning models such as a random forest to predict prices of wines based on the descriptions; this would be a useful model for new, budding (pun intended) wineries to determine the price. Additionally, this data is only a fraction of the data found on the Wine Enthusiast website. Overall, creativity is the only limitation on what other problems we could solve with this dataset.

The data used is just a fraction of the data found on the Wine Enthusiast website, the team has created a script to collect additional information on the site, which can be found here for future expansion.

Datasets Cited

Special thanks to Zack Thoutt for providing us with dataset_1 and Sanyam Kapoor for providing us with dataset_2.

The data we used is available at: https://github.com/C4rbyn3m4n/wine_reviews_data_analysis/tree/master/data

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIGFuZCBBbmFseXppbmcgV2luZSBFbnRodXNpYXN0IFJldmlld3MiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRoZW1lOiB1bml0ZWQKLS0tCiMjIFRlYW0gbWVtYmVycwoqKlNhaXZhbXNpIEhhbnVtYW50aHUqKiA8YnI+IFtWYW1zbyB0aGUgRXh0cmFvcmRpbmFpcmVdKG1haWx0bzpzaGFudW1hbnRodTQwMTZAZmxvcmlkYXBvbHkuZWR1KSAKCioqT3Nha2kgUG9raW1hKiogPGJyPiBbU2FraS1TYWtpXShtYWlsdG86b3Bva2ltYTUwNjFAZmxvcmlkYXBvbHkuZWR1KSAKCioqSXNhYmVsIFppbW1lcm1hbioqIDxicj4gW1ogQmVzdF0obWFpbHRvOml6aW1tZXJtYW41Mjk4QGZsb3JpZGFwb2x5LmVkdSkKCiMgSW50cm9kdWN0aW9uClR1cm5pbmcgMjEgaXMgYSBtaWxlc3RvbmUgaW4gZXZlcnlvbmUncyBsaWZlIGFuZCB1c3VhbGx5LCBwZW9wbGUgY2VsZWJyYXRlIGl0IHdpdGggYSBmYW1pbHkgZGlubmVyIGFuZCBnbGFzcyBvZiB3aW5lLiBCdXQsIGhvdyBkb2VzIG9uZSBmaW5kIHRoZSBiZXN0IHdpbmUgY29uc2lkZXJpbmcgdGhhdCBwZXJzb24gaGFzIG5ldmVyIGhhZCBhbGNvaG9sLiAKCi0gRG8gdGhleSBnZXQgdGhlIGJlc3QgcmF0ZWQvbW9zdCBleHBlbnNpdmUgYm90dGxlPyAoVG9vIGJhZCBjb2xsZWdlIHN0dWRlbnRzIGFyZSBwb29yKSAKLSBEbyB0aGV5IHRyeSB0byBmaW5kIHRoZSBiZXN0IHZhbHVlIHdpbmU/IChCYWxsaW4nIG9uIGEgYnVkZ2V0KQoKLSBPciBkbyB0aGV5IGZvbGxvdyBKZXNzIGZyb20gTmV3IEdpcmw/IChPYnZpb3VzbHksIHRoZSBvbmx5IG9wdGlvbikKCkxldCdzIHRha2UgYSBkZWVwIGRpdmUgYW5kIGFuc3dlciB0aGVzZSBwcmVzc2luZyBxdWVzdGlvbnMhCgpPdXIgcHJvamVjdCBpbnZlc3RpZ2F0ZXMgfjEyMCwwMDAgd2luZSByZXZpZXdzIHdpdGggY2hhcmFjdGVyaXN0aWNzIHN1Y2ggYXMgdmFyaWV0eSwgdmludGFnZSwgd2luZXJ5LCBwcmljZSwgZXRjLiBUaGlzIGlzIG91ciBGaW5hbCBQcm9qZWN0IGZvciBJbnRybyB0byBEYXRhIFNjaWVuY2UgKFNwcmluZyAyMDIwKSBjbGFzcywgYW5kIHdlIHRob3VnaHQgaXQgd2FzIGEgYml0IHRvbyBlYXN5LCBzbyB3ZSBkZWNpZGVkIHRvIGV4cGFuZCBpdCBhbmQgdGFrZSBpdCBhIHN0ZXAgZnVydGhlci4KCiMgUHJlcmVxdWlzaXRlcwoKTG9hZGluZyB0aGUgcmVxdWlyZWQgcGFja2FnZXMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpgYGAKCiMgRGF0YXNldAoKSW1wb3J0IHByb2Nlc3NlZCBkYXRhLCB3aGljaCBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vcmVpLWZhdmVzLXdpbmUtZGF0YS1hbmFseXNpcy5uZXRsaWZ5LmFwcC9kYXRhL3Byb2Nlc3NlZF9kYXRhL3ByZXByb2Nlc3NpbmcubmIuaHRtbCkuIE5vdGU6IFRoaXMgZmlsZSBhbHNvIGNyZWRpdHMgb3VyIGRhdGFzZXQgY3VyYXRvcnMgYW5kIG91dGxpbmVzIG91ciBkYXRhIGNsZWFuaW5nIHBpcGVsaW5lLgoKYGBge3J9CiNyZWFkIHByZXByb2Nlc3NlZCBkYXRhCndpbmVzIDwtIHJlYWQuY3N2KGZpbGUgPSAnLi4vZGF0YS9wcm9jZXNzZWRfZGF0YS93aW5lcy5jc3YnKQpgYGAKCkdldCBhIHNhbXBsZSBvZiB0aGUgZGF0YXNldCBmb3IgZWFzeSB0ZXN0aW5nLiBIb3dldmVyLCBmb3Igb3VyIGZpbmFsIHJlcG9ydCwgd2UgcmFuIGFsbCBvZiBvdXIgY29kZSBvbiB0aGUgZnVsbCB3aW5lcyBkYXRhc2V0IHRvIGdldCBtb3JlIGFjY3VyYXRlIHJlc3VsdHMuCmBgYHtyfQojc2V0IHNlZWQgdmFsdWUgdG8gYmlydGhkYXkgb2YgUmljYXJkbyBSb2RyaWd1ZXosIEFtZXJpY2FuIHdyZXN0bGVyIGFuZCByaW5nIGFubm91bmNlciBhbmQgRHIuIFJlaW5hbGRvIChSZWkpIFNhbmNoZXotQXJpYXMKc2V0LnNlZWQoMTk2MzAyMTcpCgojc2V0IHBlcmNlbnRhZ2UgdG8gdGVzdCB3aXRoIGZvciBzaW1wbGljaXR5LCBpZiBuZWVkZWQKcGVyY2VudGFnZSA8LSA1CndpbmVfc2FtcGxlPC0gc2FtcGxlX24od2luZXMsIHBlcmNlbnRhZ2UvMTAwKm5yb3cod2luZXMpKQpgYGAKCiMjIFJhdGluZyBDbGFzc2lmaWNhdGlvbgoKV2luZXMgYXJlIG5vcm1hbGx5IGNsYXNzaWZpZWQgaW4gY2F0ZWdvcmllcyBhcyBmb3VuZCBvbiB0aGlzIFt3ZWJzaXRlXShodHRwczovL3d3dy53aW5lbWFnLmNvbS8yMDEwLzA0LzA5L3lvdS1hc2tlZC1ob3ctaXMtYS13aW5lcy1zY29yZS1kZXRlcm1pbmVkLykuIFRvIGNyZWF0ZSBhIG1vcmUgcmljaCBkYXRhc2V0IHdlIGFkZGVkIHRoZSBmaWVsZCBgcmF0aW5nX2NhdGVnb3J5YCBkZXRlcm1pbmVkIGFzOgoKfENhdGVnb3J5ICB8IFJhdGluZyAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnxDbGFzc2ljICAgfAk5OC0xMDAgfCBUaGUgcGlubmFjbGUgb2YgcXVhbGl0eS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8U3VwZXJiICAgIHwJOTQtOTcJIHwgQSBncmVhdCBhY2hpZXZlbWVudC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfEV4Y2VsbGVudCB8CTkwLTkzCSB8IEhpZ2hseSByZWNvbW1lbmRlZC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnxWZXJ5IEdvb2QgfCAgODctODkJIHwgT2Z0ZW4gZ29vZCB2YWx1ZTsgd2VsbCByZWNvbW1lbmRlZC4gICAgICAgICAgICAgICAgICAgIHwKfEdvb2QJICAgICB8ICA4My04NgkgfCBTdWl0YWJsZSBmb3IgZXZlcnlkYXkgY29uc3VtcHRpb247IG9mdGVuIGdvb2QgdmFsdWUuICAgfAp8QWNjZXB0YWJsZXwJODAtODIJIHwgQ2FuIGJlIGVtcGxveWVkIGluIGNhc3VhbCwgbGVzcy1jcml0aWNhbCBjaXJjdW1zdGFuY2VzIHwKClRoaXMgYWxsb3dzIHRvIHRha2UgYSBxdWFudGl0YXRpdmUgZGF0YSBhbmQgdHVybiBpdCBpbnRvIHF1YWxpdGF0aXZlIGRhdGEsIHdoaWNoIGNhbiBiZSB1c2VkIGZvciBkYXRhIHZpc3VhbGl6YXRpb24gbGF0ZXIuIAoKYGBge3J9CiMgZnVuY3Rpb24gdG8gYWRkIHJhdGluZwpyYXRpbmdfY2F0ZWdvcnkgPC0gZnVuY3Rpb24ocG9pbnRzKXsKICBpZihwb2ludHM+PTk4KXsKICAgIHJldHVybigiQ2xhc3NpYyIpCiAgfQogIGVsc2UgaWYgKHBvaW50cz49OTQpewogICAgcmV0dXJuKCJTdXBlcmIiKQogIH0KICBlbHNlIGlmKHBvaW50cz49OTApewogICAgcmV0dXJuKCJFeGNlbGxlbnQiKQogIH0KICBlbHNlIGlmKHBvaW50cz49ODcpewogICAgcmV0dXJuKCJWZXJ5IEdvb2QiKQogIH0KICBlbHNlIGlmKHBvaW50cz49ODMpewogICAgcmV0dXJuKCJHb29kIikKICB9CiAgZWxzZXsKICAgIHJldHVybigiQWNjZXB0YWJsZSIpCiAgfQp9Cgp3aW5lczwtIHdpbmVzICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUocmF0aW5nX2NhdGVnb3J5ID0gcmF0aW5nX2NhdGVnb3J5KHBvaW50cykpCmhlYWQod2luZXMpCmBgYAojIyBDcmVhdGUgYSBSZXZpZXdlciBQcm9maWxlCkVhY2ggcmV2aWV3ZXIgaGFzIHRoZXJlIG93biBiaWFzLiBJbiBvcmRlciB0byBvZmZzZXQgdGhhdCB3ZSBtYWRlIGEgInByb2ZpbGUiIGZvciBlYWNoIHJldmlld2VyLiBUaGlzIHByb2ZpbGUgYWxsb3dzIHVzIHRvIGxhdGVyIG5vcm1hbGl6ZSB0aGUgd2luZSBwb2ludHMgZm9yIG1vcmUgcm9idXN0IGFwcGxlcyB0byBhcHBsZXMgY29tcGFyaXNvbi4KCiMjIyBTcGxpdCBUYXN0ZXIgRGF0YQpUbyBtYWtlIG91ciBkYXRhZnJhbWVzIG1vcmUgbWFuYWdhYmxlIHdlIHNwbGl0IHJlZHVudGFudCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdGFzdGVycyBpbnRvIGEgbmV3IGRhdGFmcmFtZS4KYGBge3J9CnRhc3RlcnMgPC0gd2luZXMgJT4lCiAgc2VsZWN0KHRhc3Rlcl9uYW1lLCB0YXN0ZXJfdHdpdHRlcl9oYW5kbGUpICU+JSAKICB1bmlxdWUoKQp0YXN0ZXJzCmBgYAoKCkRyb3AgYHRhc3Rlcl90d2l0dGVyX2hhbmRsZWAgaW4gd2luZXMgZGF0YWZyYW1lIHRvIHJlZHVjZSByZWR1bnRhbnQgaW5mb3JtYXRpb24uCmBgYHtyfQp3aW5lcyA8LSB3aW5lcyAlPiUKICBzZWxlY3QoLXRhc3Rlcl90d2l0dGVyX2hhbmRsZSkKaGVhZCh3aW5lcykKYGBgCgojIyMgQ3JlYXRlIFJldmlld2VyIE1ldHJpY3MKRWFjaCByZXZpZXdlcnMgcHJvZmlsZSBpbmNsdWRlcyB0aGUgZm9sbG93aW5nIG1ldHJpY3MgdG8gY3JlYXRlIGEgcHJvZmlsZTogCgotIGBhdmdfcG9pbnRzYCB3aGljaCBpcyB0aGUgYXZlcmFnZSBvZiBhbGwgdGhlIHJldmlld2VyJ3Mgc2NvcmVzCgotIGBzZF9wb2ludHNgIHdoaWNoIGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYWxsIHRoZSByZXZpZXdlcidzIHNjb3JlcwoKLSBgdmFyX3BvaW50c2Agd2hpY2ggaXMgdGhlIHZhcmlhbmNlIG9mIGFsbCB0aGUgcmV2aWV3ZXIncyBzY29yZXMKCi0gYHJldmlld3NgIHdoaWNoIGlzIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBjb25kdWN0ZWQKCmBgYHtyfQp0YXN0ZXJfcmF0aW5nX3Byb2ZpbGUgPC0gd2luZXMgJT4lCiAgZ3JvdXBfYnkodGFzdGVyX25hbWUpICU+JQogIHN1bW1hcml6ZSgKICAgIGF2Z19wb2ludHMgPSBtZWFuKHBvaW50cyksCiAgICBzZF9wb2ludHMgPSBzZChwb2ludHMpLAogICAgdmFyX3BvaW50cyA9IHZhcihwb2ludHMpLAogICAgcmV2aWV3cyA9IG4oKQogICkKCnRhc3RlcnMgPC0gaW5uZXJfam9pbih0YXN0ZXJzLCB0YXN0ZXJfcmF0aW5nX3Byb2ZpbGUsIGJ5ID0gInRhc3Rlcl9uYW1lIikKaGVhZCh0YXN0ZXJzKQpgYGAKCgojIyBOb3JtYWxpemVkIFBvaW50cwpBcyBtZW50aW9uZWQsIGVhY2ggcmV2aWV3ZXIgaGFzIGEgZGlmZmVyZW50IGJpYXMuIFRvIG9mZnNldCB0aGlzLCB3ZSBjcmVhdGVkIGEgbm9ybWFsaXplZCBtZXRyaWMsIGBub3JtX3BvaW50c2AsIGJ5IGxvb2tpbmcgYXQgdGhlIG51bWJlciBvZiBzdGFuZGFyZCBkZXZpYXRpb25zIGEgd2luZSBpcyBmcm9tIHRoZSByZXZpZXdlcidzIGBhdmdfcG9pbnRzYC4gVGhpcyBnaXZlcyB1cyBhIG1vcmUgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2Ygd2hpY2ggd2luZXMgYXJlICJiZXR0ZXIiIHRoYW4gdGhlIHJlc3QuCgpgYGB7cn0Kbm9ybWFsaXplX3BvaW50cyA8LSBmdW5jdGlvbihkYXRhKXsKICBsZWZ0X2pvaW4oZGF0YSwgdGFzdGVycywgYnkgPSAidGFzdGVyX25hbWUiKSU+JQogICAgcm93d2lzZSgpICU+JQogICAgbXV0YXRlKG5vcm1fcG9pbnRzID0gKHBvaW50cy1hdmdfcG9pbnRzKS9zZF9wb2ludHMpICU+JQogICAgc2VsZWN0KC1hdmdfcG9pbnRzLCAtc2RfcG9pbnRzLCAtdmFyX3BvaW50cywgLXRhc3Rlcl90d2l0dGVyX2hhbmRsZSwgLXJldmlld3MpCn0KCndpbmVzIDwtIG5vcm1hbGl6ZV9wb2ludHMod2luZXMpCmhlYWQod2luZXMpIApgYGAKCiMjIERhdGEgU2FuaXRhdGlvbgpUbyBlbnN1cmUgdGhlIGludGVncml0eSBvZiBvdXIgZGF0YSwgd2UgcGVyZm9ybSBzb21lIGhhcmQgY2hlY2tzIHRoYXQgY291bGQgaGF2ZSBiZWVuIG1pc3NlZCBkdXJpbmcgb3VyIGRhdGEgcHJlLXByb2Nlc3NpbmcuClwKXApWaW50YWdlIHNlZW1zIHRvIGhhdmUgeWVhciA3MjAwLCBzbyB3ZSBmaWx0ZXJlZCBhbGwgZGF0YSB1cCB0byAyMDE5CmBgYCB7cn0Kd2luZXMgPC0gd2luZXMgJT4lCiAgZmlsdGVyKHZpbnRhZ2U8MjAxOSkKYGBgCgojIERhdGEgRXhwbG9yYXRpb24KQmVmb3JlLCBjb25kdWN0aW5nIGFueSBkZXRhaWxlZCBhbmFseXNpcyBvZiBvdXIgZGF0YXNldCwgd2UgbG9va2VkIGF0IGEgcXVpY2sgc3VtbWFyeSBvZiB0aGUgZGF0YXNldApgYGB7cn0Kc3VtbWFyeSh3aW5lcykKYGBgCgojIyBVbml2YXJpYXRlIEV4cGxvcmF0aW9uClRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGEsIHdlIGRpZCBzb21lIHNpbXBsZSB1bml2YXJpYXRlIHZpc3VhbGl6YXRpb24gYmFzZWQgb24gc3BlY2lmaWMgZmllbGRzLiBBZGRpdGlvbmFsbHksIGJlZm9yZSBkb2luZyBhIG11bHRpdmFyaWF0ZSBhbmFseXNpcyBhbmQgYW5zd2VyaW5nIG91ciByZXNlYXJjaCBxdWVzdGlvbnMsIHdlIGZpcnN0IHdhbnQgdG8gZW5zdXJlIG91ciBkYXRhc2V0IGlzIHJvYnVzdCBhbmQgYW4gYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIHJlYWwgd29ybGQuCgojIyMgQWxjb2hvbCBBbW91bnQKVGhlIHZpc3VhbGl6YXRpb24gYmVsb3cgZGVwaWN0cyB0aGUgZGlzdHJpYnV0aW9uIG9mIG91ciBkYXRhc2V0IGJhc2VkIG9uIGFsY29ob2wgcGVyY2VudGFnZSwgYGFsY29ob2xgLiBUbyBiZXR0ZXIgdW5kZXJzdGFuZCBhbmQgdmlzdWFsaXplIHRoZSBkYXRhLCB3ZSBjYXRlZ29yaXplZCB0aGUgZ3JhcGggYmFzZWQgb24gYHJhdGluZ19jYXRlZ29yeWAuIE5vdGljZSwgYSBtYWpvcml0eSBvZiB3aW5lcyBoYXZlIGFuIGFsY29ob2wgYW1vdW50IGJldHdlZW4gMTIlLTE1JSwgYW5kIGFjY29yZGluZyB0byBbUmVhbCBTaW1wbGVdKGh0dHBzOi8vd3d3LnJlYWxzaW1wbGUuY29tL2hvbGlkYXlzLWVudGVydGFpbmluZy9lbnRlcnRhaW5pbmcvZm9vZC1kcmluay9hbGNvaG9sLWNvbnRlbnQtd2luZSksIHdpbmUgYWxjb2hvbGljIGNvbnRlbnQgYXZlcmFnZXMgYmV0d2VlbiAxMSUtMTMlLiBUaGlzIGxlYWRzIHVzIHRvIGJlbGlldmUgb3VyIGRhdGEgaXMgYW4gYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIHJlYWwgd29ybGQuCmBgYHtyfQp3aW5lcyAlPiUgCiAgZ3JvdXBfYnkoYWxjb2hvbCkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbSgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSBhbGNvaG9sLCAKICAgICAgZmlsbCA9IHJhdGluZ19jYXRlZ29yeSksCiAgICBuYS5ybSA9IFRSVUUsCiAgICBiaW5zID0gNTApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBicmVha3MgPSBzZXEoMCwyNSwxKSwgCiAgICBsaW1pdHMgPSBjKDQsMjIpKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBBbGNvaG9sIFBlcmNlbnRhZ2UiLAogICAgeCA9ICJBbGNvaG9sIFBlcmNlbnRhZ2UiLAogICAgeSA9ICJDb3VudCIsCiAgICBmaWxsID0gIlJhdGluZyBDYXRlZ29yeSIKICApCmBgYAoKIyMjIFZpbnRhZ2UKTmV4dCwgd2Ugd2FudGVkIHRvIHNlZSB3aGF0IHZpbnRhZ2UgbW9zdCBvZiB0aGUgd2luZXMgaW4gdGhlIGRhdGFzZXQgd2VyZS4gQWdhaW4gdG8gYmV0dGVyIHVuZGVyc3RhbmQgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YSwgd2UgY2F0ZWdvcml6ZWQgdGhlIGdyYXBoIGJhc2VkIG9uIGByYXRpbmdfY2F0ZWdvcnlgLiBOb3RpY2UgdGhhdCB0aGVyZSBpcyByb3VnaGx5IGFuIGVxdWFsIHBlcmNlbnRhZ2Ugb2YgZWFjaCByYXRpbmcgY2F0ZWdvcnkgcGVyIHZpbnRhZ2UuCgooTm90ZTogRGF0YSBwb2ludHMgYmVmb3JlIDE5OTAgaGF2ZSBiZWVuIG9taXR0ZWQgZm9yIGNsYXJpdHkgaW4gdmlzdWFsaXphdGlvbikKYGBge3J9CndpbmVzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2JhcigKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHg9dmludGFnZSwgCiAgICAgIGZpbGwgPSByYXRpbmdfY2F0ZWdvcnkpLAogICAgbmEucm0gPSBUUlVFKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgYnJlYWtzID0gc2VxKDE5OTAsMjAxOSw1KSwgCiAgICBsaW1pdHMgPSBjKDE5OTAsMjAxOSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFZpbnRhZ2UiLAogICAgeCA9ICJWaW50YWdlIiwgCiAgICB5ID0gIkNvdW50IiwKICAgIGZpbGwgPSAiUmF0aW5nIENhdGVnb3J5IikKYGBgCgojIyMgV2luZXJ5ClRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBudW1iZXIgd2luZXMgcGVyIHdpbmVyeSwgd2UgZGlkIGEgdmlzdWFsaXphdGlvbiB0aGF0IGNvdW50cyB0aGUgbnVtYmVyIG9mIHdpbmVzIHBlciB3aW5lcnkgc2hvd2luZyBvbmx5IFRvcCAxMCB3aW5lcmllcyB0byBnaXZlIHlvdSBhbiBpZGVhIG9mIHdoYXQgd2luZXJ5IGhhcyB0aGUgbW9zdCBzZWxlY3Rpb24gb2Ygd2luZXMuIE5vdGljZSwgZWFjaCBvZiB0aGUgdG9wIDEwIHByb2R1Y2VycyBvZiB3aW5lIGhhdmUgb3ZlciAxMDAgZGlmZmVyZW50IHdpbmUgbGFiZWxzLgpgYGB7cn0Kd2luZXMgJT4lCiAgZ3JvdXBfYnkod2luZXJ5KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIGdncGxvdCgpICsKICBnZW9tX2NvbCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHg9IHJlb3JkZXIod2luZXJ5LCBjb3VudCksCiAgICAgIHkgPSBjb3VudCwKICAgICAgZmlsbCA9IHdpbmVyeSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdpbmVyeSAoVG9wIDEwKSIsCiAgICB4ID0gIldpbmVyeSIsIAogICAgeSA9ICJDb3VudCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCgojIyMgUHJvdmluY2UKVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIG51bWJlciBvZiB3aW5lcyBwZXIgcHJvdmluY2UsIHdlIGRpZCBhIHZpc3VhbGl6YXRpb24gdGhhdCBjb3VudHMgdGhlIG51bWJlciBvZiB3aW5lcyBwZXIgcHJvdmluY2UsIHNob3dpbmcgb25seSB0aGUgdG9wIDEwIHByb3ZpbmNlcyB3aXRoIHRoZSBtb3N0IHdpbmVzLiBUaGlzIGNhbiBnaXZlIHRoZSByZWFkZXIgYW4gaWRlYSB3aGVyZSB0aGVpciB3aW5lIHdpbGwgbW9zdCBsaWtlbHkgYmUgbWFkZSB3aXRoIENhbGlmb3JuaWEgc3RhbmRpbmcgb3V0IGFzIGEgY2xlYXIgbGVhZGVyLgpgYGB7cn0Kd2luZXMgJT4lIAogIGdyb3VwX2J5KHByb3ZpbmNlKSAlPiUgCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lIAogIHNsaWNlKDE6MTApICU+JSAKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSByZW9yZGVyKHByb3ZpbmNlLCBjb3VudCksIAogICAgICB5ID0gY291bnQsCiAgICAgIGZpbGwgPSBwcm92aW5jZSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFByb3ZpbmNlIChUb3AgMTApIiwKICAgIHggPSAiUHJvdmluY2UiLCAKICAgIHkgPSAiQ291bnQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAojIyMgV2luZSBDYXRlZ29yeQpOZXh0LCB3ZSB3YW50ZWQgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgZGlmZmVyZW50IHdpbmUgY2F0ZWdvcmllcyBpbiBvdXIgZGF0YXNldC4gVG8gYmV0dGVyIHVuZGVyc3RhbmQgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YSwgd2UgY2F0ZWdvcml6ZWQgdGhlIGdyYXBoIGJhc2VkIG9uIGByYXRpbmdfY2F0ZWdvcnlgLiBOb3RpY2UsIGEgbWFqb3JpdHkgb2YgdGhlIHdpbmVzIGFyZSByZWQgb3Igd2hpdGUgd2luZXMuCgpgYGB7cn0Kd2luZXMgJT4lIAogIGdncGxvdCgpKwogIGdlb21fYmFyKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IGNhdGVnb3J5LAogICAgICBmaWxsID0gcmF0aW5nX2NhdGVnb3J5KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgV2luZSBDYXRlZ29yeSIsCiAgICB4ID0gIldpbmUgQ2F0ZWdvcnkiLCAKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJSYXRpbmcgQ2F0ZWdvcnkiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMjIFByaWNlCk5leHQsIHdlIHdhbnRlZCB0byB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmljZSBpbiBvdXIgZGF0YXNldC4gVG8gYmV0dGVyIHVuZGVyc3RhbmQgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YSwgd2UgY2F0ZWdvcml6ZWQgdGhlIGdyYXBoIGJhc2VkIG9uIGByYXRpbmdfY2F0ZWdvcnlgLiBOb3RpY2UsIGEgbWFqb3JpdHkgb2Ygd2luZXMgYXJlIFwkNTAgYW5kIGJlbG93LCB3aXRoIHRoZSBtb3N0IGNvbW1vbiBiZWluZyBiZXR3ZWVuIFwkMTIgLSBcJDI1LiBBZ2FpbiwgdGhpcyBhY2N1cmF0ZWx5IHJlcHJlc2VudHMgdGhlIHJlYWwgd29ybGQuIEFzIHN0YXRlZCBieSBbVml2aW5vXShodHRwczovL3d3dy52aXZpbm8uY29tL3dpbmUtbmV3cy9ob3ctbXVjaC1kb2VzLWEtZ29vZC1ib3R0bGUtb2Ytd2luZS1jb3N0KSwgdGhlIGF2ZXJhZ2UgcHJpY2UgZm9yIGEgZ29vZCBib3R0bGUgb2YgcmVkL3doaXRlIHdpbmUgaXMgflwkMTUgYW5kIH5cJDI4IGZvciBhIHZlcnkgZ29vZCBib3R0bGUuIChDQVVUSU9OOiBUaGUgVml2aW5vIHByaWNlcyBkZW5vdGVkIHdlcmUgc2ltcGx5IGFuIGF2ZXJhZ2UgZm9yIHJlZC93aGl0ZSB3aW5lcyBhdmVyYWdlIGNvc3RzLiBUaGlzIHdhcyBkb25lIHRvIGdlbmVyYWxpemUgdGhlIGluZm9ybWF0aW9uIHRvIG1ha2UgYSBzaW1wbGUgY29tcGFyaXNvbi4gQWxzbywgdGhpcyBsaW1pdGVkIHRvIHJlZC93aGl0ZSB3aW5lIGFuZCBkb2VzIG5vdCBhY2N1cmF0ZWx5IGluY2x1ZGUgb3RoZXIgdHlwZXMsIHdoaWNoIGFyZSBpbmNsdWRlZCB3aXRoaW4gb3VyIGRhdGFzZXQpCgooTm90ZTogRGF0YSBwb2ludHMgYWJvdmUgJDQwMCBoYXZlIGJlZW4gb21pdHRlZCBmb3IgY2xhcml0eSBpbiB2aXN1YWxpemF0aW9uKQpgYGB7cn0Kd2luZXMgJT4lIAogIGZpbHRlcihwcmljZSA8IDQwMCkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbSgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHg9cHJpY2UsIAogICAgICBmaWxsID0gcmF0aW5nX2NhdGVnb3J5KSwKICAgIGJpbndpZHRoID0gMTUpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFByaWNlIiwKICAgIHggPSAiUHJpY2UiLCAKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJSYXRpbmcgQ2F0ZWdvcnkiKQpgYGAKCiMjIyAgUG9pbnRzIApOZXh0LCB3ZSB3YW50ZWQgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgcG9pbnRzIGluIG91ciBkYXRhc2V0LiBOb3RpY2UsIGhlcmUgdGhhdCBhIG1ham9yaXR5IG9mIHdpbmVzIHJlY2VpdmUgYSBzY29yZSBiZXR3ZWVuIDg3IGFuZCA5MC4gV2hpY2ggaXMgYWNjdXJhdGUgdG8gdGhlIGluZm9ybWF0aW9uIHByb3ZpZGVkIG9uIFtXaW5lIFNlYXJjaGVyXShodHRwczovL3d3dy53aW5lLXNlYXJjaGVyLmNvbS9jcml0aWNzLTE3LXdpbmUrZW50aHVzaWFzdCksIHdoaWNoIHN0YXRlcyA1MCUgb2YgdGhlIHNjb3JlcyBmYWxsIGJldHdlZW4gODYtOTAgcG9pbnQgZnJvbSBXaW5lIEVudGh1c2lhc3QgcmF0aW5ncy4KCihOb3RlOiBXZSBvdXIgZGF0YXNldCB3YXMgcmV0ZXJpdmVkIGZyb20gdGhlIFdpbmUgRW50aHVzaWFzdCB3ZWJzaXRlKQpgYGB7cn0Kd2luZXMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKAogICAgbWFwcGluZyA9IGFlcyh4PXBvaW50cyksCiAgICBiaW5zID0gMjApICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFBvaW50cyIsCiAgICB4ID0gIlBvaW50cyIsIAogICAgeSA9ICJDb3VudCIpCmBgYAoKIyMgTXVsdGl2YXJpYXRlIEV4cGxvcmF0aW9uCk5vdyB0aGF0IHdlIGhhdmUgYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiBvdXIgZGF0YSwgYW5kIHdlIGtub3cgaXQgaXMgYW4gYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIHJlYWwgd29ybGQsIHdlIGNhbiBwZXJmb3JtIGEgbW9yZSBkZXRhaWxlZCBhbmFseXNpcyB1c2luZyBtdWx0aXBsZSB2YXJpYWJsZXMuCgojIyMgUG9pbnRzIGJ5IFRhc3RlcgpUbyB1bmRlcnN0YW5kIHRoZSBwb2ludCBkaXN0cmlidXRpb24gYnkgdGFzdGVycywgd2UgZGlkIGEgbXVsdGl2YXJpYXRlIHZpc3VhbGl6YXRpb24gdGhhdCBjb3JyZWxhdGVzIHRhc3RlciBuYW1lcyBiYXNlZCBvbiB0aGUgYXZlcmFnZSB3aW5lIHBvaW50cyBhcyBpZGVudGlmaWVkIGJ5IHRoZSB4LWludGVyY2VwdC4gVGhpcyBnaXZlcyB0aGUgcmVhZGVyIGFuIGlkZWEgb2YgaG93IHNvbWUgcmV2aWV3ZXJzIGNvcnJlbGF0ZSB0byB0aGUgb3ZlcmFsbCBhdmVyYWdlLgoKKE5vdGU6IFRoZSDigJxibGFua+KAnSByZXByZXNlbnRzIHVua25vd24gcmV2aWV3ZXJzLiBXZSBhc3N1bWVkIHRoZSByZXZpZXdlcnMgbm90IG5hbWVkIGhhdmUgbm90IHJhdGVkIGEgc2lnbmlmaWNhbnQgYW1vdW50IG9mIHdpbmVzIGFuZCBjYW4gYmUgZ3JvdXBlZCBpbnRvIGEgc2luZ3VsYXIgcmV2aWV3ZXIpCmBgYHtyfQp3aW5lcyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeD10YXN0ZXJfbmFtZSwKICAgICAgeT1wb2ludHMsIAogICAgICBjb2xvciA9IHRhc3Rlcl9uYW1lKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4od2luZXMkcG9pbnRzKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgbGFicygKICAgIHRpdGxlID0gIlBvaW50cyBieSBUYXN0ZXIiLAogICAgeD0iVGFzdGVyIE5hbWUiLAogICAgeT0iUG9pbnRzIgogICkrCiAgY29vcmRfZmxpcCgpCmBgYAoKCiMjIyBQcmljZSBieSBQb2ludHMKVG8gdW5kZXJzdGFuZCB0aGUgcHJpY2UgZGlzdHJpYnV0aW9uIGJ5IHBvaW50cywgd2UgZGlkIGEgbXVsdGl2YXJpYXRlIHZpc3VhbGl6YXRpb24gdGhhdCBjcmVhdGVzIGEgc2NhdHRlciBwbG90IG9mIHRoZSB3aW5lcyBiYXNlZCBvbiBwb2ludHMgYW5kIHByaWNlLiBUaGVuLCB3ZSBhZGRlZCBhIHNtb290aCB0cmFuc2Zvcm1hdGlvbiB0byBpZGVudGlmeSB0cmVuZHMuIE5vdGljZSwgdGhlIGRhdGEgaXMgc3RhY2tlZCwgYW5kIHRoZSBzY29yZXMgcmFuZ2UgZnJvbSA4MC0xMDAKYGBge3J9CndpbmVzICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSBwb2ludHMsIAogICAgICB5ID0gcHJpY2UsIAogICAgICBjb2xvciA9IGNhdGVnb3J5KSwKICAgIG5hLnJtID0gVFJVRSwKICAgIGFscGhhID0gMC4yKSArCiAgbGFicygKICAgIHRpdGxlID0gIlByaWNlIGJ5IFBvaW50cyIsIAogICAgeCA9ICJQb2ludHMiLAogICAgeSA9ICJQcmljZSIsCiAgICBjb2xvciA9ICJXaW5lIENhdGVnb3J5IikgKwogIGdlb21fc21vb3RoKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHBvaW50cywgCiAgICAgIHkgPSBwcmljZSksCiAgICBuYS5ybSA9IFRSVUUpCmBgYAoKU2luY2UgdGhlcmUgYXJlIG11bHRpcGxlIG91dGxpZXJzLCBhbmQgdGhlIHZpc3VhbGl6YXRpb24gaXMgY2x1c3RlcmVkLiBCeSBwZXJmb3JtaW5nIGEgbG9nIG9uIGFsbCB0aGUgcHJpY2VzLCB3ZSBjYW4gcmVkdWNlIHRoZSBza2V3bmVzcyBvZiB0aGUgdmlzdWFsaXphdGlvbi4gTm90aWNlLCBhcyB0aGUgcXVhbGl0eSBvZiB3aW5lIGluY3JlYXNlcywgcHJpY2UgaW5jcmVhc2VzIGV4cG9uZW50aWFsbHkuCmBgYHtyfQp3aW5lcyAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcG9pbnRzLCAKICAgICAgeSA9IGxvZyhwcmljZSksIAogICAgICBjb2xvciA9IGNhdGVnb3J5KSwKICAgIG5hLnJtID0gVFJVRSwKICAgIGFscGhhID0gMC4yKSArCiAgbGFicygKICAgIHRpdGxlID0gImxvZyhQcmljZSkgYnkgUG9pbnRzIiwgCiAgICB4ID0gIlBvaW50cyIsCiAgICB5ID0gImxvZyhQcmljZSkiLAogICAgY29sb3IgPSAiV2luZSBDYXRlZ29yeSIpICsKICBnZW9tX3Ntb290aCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSBwb2ludHMsIAogICAgICB5ID0gbG9nKHByaWNlKSksCiAgICBuYS5ybSA9IFRSVUUpCmBgYAoKIyMjIyBHcm91cCBieSBXaW5lIENhdGVnb3J5Ck5leHQsIG91ciBncm91cCB3YW50ZWQgdG8gZG8gYSBtb3JlIGdyYW51bGFyIGFuYWx5c2lzIGJ5IGxvb2tpbmcgYXQgaG93IHRoZSBwcmljZSB2YXJpZXMgYnkgcG9pbnRzIGdyb3VwZWQgYnkgdGhlIHdpbmUgY2F0ZWdvcnkuIE5vdGljZSwgYWxsIHRoZSBwcmljZXMgZ28gdXAgYXMgcG9pbnRzIGdvIHVwLCBidXQgdGhlIGdyb3d0aCByYXRlcyBhcmUgZGlmZmVyZW50IHBlciB3aW5lIGNhdGVnb3J5LgpgYGB7cn0Kd2luZXMgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHBvaW50cywgCiAgICAgIHkgPSBsb2cocHJpY2UpLCAKICAgICAgY29sb3IgPSBjYXRlZ29yeSksCiAgICBhbHBoYSA9MC4yLAogICAgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV9zbW9vdGgoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcG9pbnRzLCAKICAgICAgeSA9IGxvZyhwcmljZSkpLAogICAgbmEucm0gPSBUUlVFKSArCiAgZmFjZXRfd3JhcCh+Y2F0ZWdvcnkpICsKICBsYWJzKAogICAgdGl0bGUgPSAibG9nKFByaWNlKSBieSBQb2ludHMiLCAKICAgIHggPSAiUG9pbnRzIiwKICAgIHkgPSAibG9nKFByaWNlKSIpKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgKQpgYGAKCgojIERhdGEgQW5hbHlzaXMKTm93IHRoYXQgd2UgZnVsbHkgdW5kZXJzdGFuZCB0aGUgZGF0YXNldCB3ZSBhcmUgd29ya2luZyB3aXRoLCB3ZSBwbGFuIHRvIGFuc3dlciBzb21lIHJlc2VhcmNoIHF1ZXN0aW9ucyBwcm9wb3NlZCBieSB0aGUgdGVhbS4KCiMjIFdoYXQgaXMgdGhlIGJlc3Qgd2luZT8KQW4gZWFzeSB3YXkgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IHdpbmUgaXMgYnkgc2ltcGx5IGZpbmRpbmcgdGhlIHRvcCAxMCB3aW5lcyBieSByYXRpbmcuCmBgYHtyfQp3aW5lcyAlPiUKICBhcnJhbmdlKGRlc2MocG9pbnRzKSkgJT4lCiAgc2xpY2UoMToxMCkgJT4lCiAgc2VsZWN0KHRpdGxlLHByaWNlLCBwb2ludHMscmF0aW5nX2NhdGVnb3J5LCBub3JtX3BvaW50cykKYGBgCgpIb3dldmVyLCB0aGlzIGRvZXMgbm90IGFjY291bnQgZm9yIHRoZSB0YXN0ZXIncyBiaWFzLiBJbnN0ZWFkLCBvdXIgZ3JvdXAgbm9ybWFsaXplZCB0aGUgcG9pbnRzIGJhc2VkIG9uIGVhY2ggdGFzdGVyIGJhc2VkIG9uIHRoZSBudW1iZXIgb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucyBhIHdpbmUgaXMgZnJvbSB0aGUgcmF0ZXJzIGF2ZXJhZ2UuIEZvciBleGFtcGxlLCBUYXN0ZXIgQSBjb3VsZCBnaXZlIGEgd2luZSAxMDAgYnV0IGhhcyBhbiBhdmVyYWdlIHJhdGluZyBzY29yZSBvZiA5NSB3aXRoIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDUuIFdoZXJlYXMsIFRhc3RlciBCIGNvdWxkIG9mZmVyIGEgd2luZSA5MSBhbmQgaGF2ZSBhbiBhdmVyYWdlIHNjb3JlIG9mIDg3IHdpdGggYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMi4gQWx0aG91Z2ggdGhlIHdpbmUgdGFzdGVkIGJ5IFRhc3RlciBBIGdvdCBhIHBlcmZlY3QgMTAwIHNjb3JlLCBUYXN0ZXIgQuKAmXMgd2luZSB3YXMgbXVjaCDigJxiZXR0ZXLigJ0gd2luZSBzaW5jZSBpdCB3YXMgdHdvIHN0YW5kYXJkIGRldmlhdGlvbnMgZnJvbSB0aGUgdGFzdGVycyBhdmVyYWdlIGNvbXBhcmVkIHRvIDEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBvdGhlciB3aW5lLgoKTG9va2luZyBhdCB0aGUgYG5vcm1fcG9pbnRzYCB0aGVzZSBhcmUgdGhlIHRvcCAxMCBiZXN0IHdpbmVzCmBgYHtyfQp3aW5lcyAlPiUKICBhcnJhbmdlKGRlc2Mobm9ybV9wb2ludHMpKSAlPiUKICBzbGljZSgxOjEwKSU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKIyMgV2hhdCBpcyB0aGUgYmVzdCB2YWx1ZSB3aW5lPwpBIHNpbXBsZSB2YWx1ZSBtZXRyaWMgd2UgY2FuIHVzZSB0byBkZXRlcm1pbmUgYmVzdCB2YWx1ZSBpcyBgcG9pbnRzL3ByaWNlYC4KYGBge3J9CndpbmVzICU+JQogIGFycmFuZ2UoZGVzYyhwb2ludHMvcHJpY2UpKSAlPiUKICBzbGljZSgxOjEwKSU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKSG93ZXZlciwgYWdhaW4gdGhpcyBtZXRyaWMgaXMgbm90IG5vcm1hbGl6ZWQuIEluc3RlYWQsIGBub3JtX3BvaW50cy9wcmljZWAgd291bGQgeWllbGQgbW9yZSByb2J1c3QgcmVzdWx0cy4KYGBge3J9CndpbmVzICU+JQogIGFycmFuZ2UoZGVzYyhub3JtX3BvaW50cy9wcmljZSkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKIyMgV2hpY2ggcHJvdmluY2UgaGFzIHRoZSBiZXN0IHdpbmU/ClRvIGRldGVybWluZSB0aGUgYmVzdCBwcm92aW5jZSBmb3Igd2luZSBieSBwb2ludHMsIHdlIGF2ZXJhZ2VkIHRoZSBwb2ludHMgb2YgYWxsIHdpbmVzIHBlciBwcm92aW5jZSB3aXRoIGEgc2FtcGxlIHNpemUgZ3JlYXRlciB0aGFuIDMwIGFuZCByZXR1cm5lZCB0aGUgdG9wIDEwIHdpdGggc3RhbmRhcmQgZXJyb3IuIE5vdGljZSBob3cgdGhlIHN0YW5kYXJkIGVycm9yIGlzIGxvdywgbWVhbmluZyB0aGUgc3ByZWFkIG9mIG91ciBkYXRhIGlzIGFsc28gc21hbGwsIGFuZCB0aGUgYXZlcmFnZSBpcyB2ZXJ5IGFjY3VyYXRlLgoKYGBge3J9CndpbmVzICU+JSAKICBncm91cF9ieShwcm92aW5jZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgYXZnX3BvaW50c19wcm92ID0gbWVhbihwb2ludHMpLCAKICAgIGNvdW50ID0gbigpLCAKICAgIHN0ZF9lcnJfcG9pbnRzX3Byb3YgPSBzZChwb2ludHMpL3NxcnQoY291bnQpKSAlPiUKICBmaWx0ZXIoY291bnQ+MzApICU+JQogIGFycmFuZ2UoZGVzYyhhdmdfcG9pbnRzX3Byb3YpKSAlPiUKICBzbGljZSgxOjEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2woCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcmVvcmRlcihwcm92aW5jZSxhdmdfcG9pbnRzX3Byb3YpLCAKICAgICAgeSA9IGF2Z19wb2ludHNfcHJvdiwKICAgICAgZmlsbCA9IHByb3ZpbmNlKSkgKwogICBnZW9tX2Vycm9yYmFyKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHByb3ZpbmNlLAogICAgICB5ID0gYXZnX3BvaW50c19wcm92LAogICAgICB5bWluID0gYXZnX3BvaW50c19wcm92IC0gc3RkX2Vycl9wb2ludHNfcHJvdiwgCiAgICAgIHltYXggPSBhdmdfcG9pbnRzX3Byb3YgKyBzdGRfZXJyX3BvaW50c19wcm92CiAgICAgICksCiAgICB3aWR0aCA9IDAuMikrCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbGltaXRzPWMoODUsOTUpLCAKICAgIG9vYiA9IHJlc2NhbGVfbm9uZSkrCiAgbGFicygKICAgICAgeCA9ICdQcm92aW5jZScsIAogICAgICB5ID0gIkF2ZXJhZ2UgUG9pbnRzIiwgCiAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgUG9pbnRzIEJ5IFByb3ZpbmNlIChUb3AgMTApIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMgV2hpY2ggd2luZSB2YXJpZXR5IGlzIHRoZSBiZXN0PwpUbyBkZXRlcm1pbmUgdGhlIGJlc3QgdmFyaWV0eSBvZiB3aW5lLCB3ZSB1c2UgdGhlIGF2ZXJhZ2UgcG9pbnRzIG9mIGFsbCB3aW5lcyBwZXIgdmFyaWV0eSB3aXRoIGEgc2FtcGxlIHNpemUgZ3JlYXRlciB0aGFuIDMwLiBUaGUgZ3JhcGggYmVsb3cgc2hvd3MgdGhlIHRvcCAxMCB2YXJpZXRpZXMgd2l0aCB0aGVpciByZXNwZWN0aXZlIHN0YW5kYXJkIGVycm9yLgoKYGBge3J9CndpbmVzICU+JSAKICBncm91cF9ieSh2YXJpZXR5KSAlPiUKICBzdW1tYXJpc2UoCiAgICBhdmdfcG9pbnRzX3ZhcmlldHkgPSBtZWFuKHBvaW50cyksIAogICAgY291bnQgPSBuKCksIAogICAgc3RkX2Vycl9wb2ludHNfdmFyaWV0eSA9IHNkKHBvaW50cykvc3FydChjb3VudCkpICU+JQogIGZpbHRlcihjb3VudD4zMCkgJT4lCiAgYXJyYW5nZShkZXNjKGF2Z19wb2ludHNfdmFyaWV0eSkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIGdncGxvdCgpICsKICBnZW9tX2NvbCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSByZW9yZGVyKHZhcmlldHksYXZnX3BvaW50c192YXJpZXR5KSwgCiAgICAgIHkgPSBhdmdfcG9pbnRzX3ZhcmlldHksCiAgICAgIGZpbGwgPSB2YXJpZXR5KSkgKwogICBnZW9tX2Vycm9yYmFyKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHZhcmlldHksCiAgICAgIHkgPSBhdmdfcG9pbnRzX3ZhcmlldHksCiAgICAgIHltaW4gPSBhdmdfcG9pbnRzX3ZhcmlldHkgLSBzdGRfZXJyX3BvaW50c192YXJpZXR5LCAKICAgICAgeW1heCA9IGF2Z19wb2ludHNfdmFyaWV0eSArIHN0ZF9lcnJfcG9pbnRzX3ZhcmlldHkKICAgICAgKSwKICAgIHdpZHRoID0gMC4yKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBsaW1pdHM9Yyg4NSw5NSksIAogICAgb29iID0gcmVzY2FsZV9ub25lKSsKICBsYWJzKAogICAgICB4ID0gJ1ZhcmlldHknLCAKICAgICAgeSA9ICJBdmVyYWdlIFBvaW50cyIsIAogICAgICB0aXRsZSA9ICJBdmVyYWdlIFBvaW50cyBCeSBWYXJpZXR5IChUb3AgMTApIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMgSmVzcyBmcm9tIE5ldyBHaXJs4oCZcyBmYXZvcml0ZSB3aW5lPwpTb3VuZHMgbGlrZSBhIHNpbGx5IHF1ZXN0aW9uLCBidXQgdGFrZSBhIGNsb3NlciBsb29rIGFuZCB5b3UnbGwgZmluZCBhbiBpbnRlcmVzdGluZyBxdWVzdGlvbiB3aXRoaW4gaXQ6ICJDYW4gd2UgZGV0ZXJtaW5lIHRoZSBiZXN0IHZhbHVlIHdpbmUgYmFzZWQgb24gaG93IG11Y2ggcGVvcGxlIGFyZSB3aWxsaW5nIHRvIHBheT8iICBXRSBTVVJFIENBTiEKCmBgYHtyfQojIElmIHlvdSB3YW50IHRvIGdldCB1c2VyIGlucHV0CiN1c2VyX3ByaWNlIDwtIHJlYWRsaW5lKHByb21wdCA9ICJIb3cgbXVjaCBhcmUgeW91IHdpbGxpbmcgdG8gc3BlbmQgb24gYSBib3R0bGU/IikKI3VzZXJfcHJpY2UgPC0gYXMuaW50ZWdlcih1c2VyX3ByaWNlKQp1c2VyX3ByaWNlPC0xMQoKd2luZXMgJT4lIAogIGZpbHRlcihwcmljZSA8PSB1c2VyX3ByaWNlKSAlPiUKICBhcnJhbmdlKGRlc2Mobm9ybV9wb2ludHMvcHJpY2UpKSAlPiUKICBzbGljZSgxOjEwKSAlPiUKICBzZWxlY3QodGl0bGUsIHByaWNlLCBwb2ludHMscmF0aW5nX2NhdGVnb3J5LCBub3JtX3BvaW50cykKYGBgCgpOb3cgYmFjayB0byB0aGUgb3JpZ25hbCBxdWVzdGlvbiB3aXRoIEplc3MKPGJyPgo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDAlO2hlaWdodDowO3BhZGRpbmctYm90dG9tOjYxJTtwb3NpdGlvbjpyZWxhdGl2ZTsiPjxpZnJhbWUgc3JjPSJodHRwczovL2dpcGh5LmNvbS9lbWJlZC8zb3N4WTNKdTZwMmpKYkJvODgiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIHN0eWxlPSJwb3NpdGlvbjphYnNvbHV0ZSIgZnJhbWVCb3JkZXI9IjAiIGNsYXNzPSJnaXBoeS1lbWJlZCIgYWxsb3dGdWxsU2NyZWVuPjwvaWZyYW1lPjwvZGl2PgoKYGBge3J9IAp3aW5lcyAlPiUgCiAgZmlsdGVyKHByaWNlIDwgMTEpICU+JQogIGZpbHRlcihjYXRlZ29yeSA9PSAiU3BhcmtsaW5nIikgJT4lCiAgZmlsdGVyKGdyZXBsKCJwaW5rIiwgdGl0bGUsIGlnbm9yZS5jYXNlID0gVCkgPT0gVCkgJT4lCiAgc2VsZWN0KHRpdGxlLHByaWNlLCBwb2ludHMscmF0aW5nX2NhdGVnb3J5LCBub3JtX3BvaW50cykKYGBgCkFzIHlvdSBjYW4gc2VlLCAiWWVsbG93IFRhaWwgMjAxNSBQaW5rIEJ1YmJsZXMgU3BhcmtsaW5nIChTb3V0aCBBdXN0cmFsaWEpIiBpcyBKZXNzJ3MgdHlwZSBvZiB3aW5lIQoKIyBDb25jbHVzaW9uCkFmdGVyIGFsbCB0aGlzIGV4cGxvcmF0aW9uLCB3ZSB3ZXJlIGFibGUgdG8gd2FsayBhd2F5IHdpdGggc29tZSBpbnNpZ2h0cy4gV2UgbGVhcm5lZCB0aGF0IHN0YW5kYXJkaXppbmcgdGFzdGVycyBnaXZlcyBhIG1vcmUgYWNjdXJhdGUgb3ZlcnZpZXcgb2Ygd2hhdCB0aGUgYmVzdCB3aW5lcyByZWFsbHkgYXJlLCB0aGF0IHBvaW50IHZhbHVlcyBhZmZlY3QgcHJpY2UsIGFuZCB0aGF0IHlvdSdyZSBtb3N0IGxpa2VseSBkcmlua2luZyB3aW5lIGZyb20gQ2FsaWZvcm5pYS4gSW4gdGhlIGVuZCwgdGhvdWdoLCB0aGlzIGV4cGxvcmF0aW9uIG9mIHdpbmUgc2hvd2VkIHVzIG1vcmUgdGhhbiBqdXN0IGhvdyB0byBkZXNpZ24gdGhlIHBlcmZlY3QgZmxpZ2h0OyB3ZSBsZWFybmVkIHRoZSBpbXBvcnRhbmNlIG9mIGRhdGEgcHJlcHJvY2Vzc2luZywgdGhlIHBvd2VyIG9mIG1pbmRmdWwgZ3JhcGhpY3MsIGhvdyB0byBtYWtlIGV2ZXJ5IGdncGxvdCBlYXN5IHRvIHVuZGVyc3RhbmQgZm9yIHRoZSByZWFkZXIgdXNpbmcgY29sb3IsIGFuZCB0aGUgcHJhY3RpY2FsaXR5IG9mIHRoZSBwaXBlIHRvb2wuIFdlIHdlcmUgYWJsZSB0byBhcHBseSBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgZXhwbG9yYXRpb24gc2tpbGxzIHN1Y2ggYXMgZmlsdGVyaW5nLCBtdXRhdGluZywgYXJyYW5naW5nLCBncmVwaW5nLCBhbmQgc2xpY2luZyBkYXRhLiBCZXlvbmQgUiBza2lsbHMsIHdlIGxlYXJuZWQgaG93IHRvIGFkYXB0IHRvIGJlaW5nIGEgcmVtb3RlIHRlYW0sIHdoaWNoIHdhcyBzaWduaWZpY2FudGx5IGhlbHBlZCB3aXRoIHRoZSB1dGlsaXphdGlvbiBvZiBHaXRIdWIuIFdlIGZlZWwgdGhpcyByZXBvcnQgZ2l2ZXMgYSB3ZWxsLXJvdW5kZWQgZGlzcGxheSBvZiB0aGUgdG9vbHMgd2Ugd2VyZSB0YXVnaHQgdGhyb3VnaG91dCB0aGUgc2VtZXN0ZXIsIGFuZCB3ZSB3ZXJlIGV4Y2l0ZWQgdG8gYnVpbGQgZnJvbSB0aGF0IGtub3dsZWRnZSBhbmQgaW50ZWdyYXRlIG90aGVyIHRvb2xzIGFzIHdlbGwuCgojIEZ1dHVyZSBXb3JrcwpUaGVyZSBpcyB1bmRvdWJ0ZWRseSBtb3JlIGV4cGxvcmF0aW9uIHRvIGJlIGRvbmUgd2l0aGluIHRoaXMgZGF0YXNldC4gCkludGVncmF0aW5nIG1vcmUgVHdpdHRlciBkYXRhIHdpdGggcGFja2FnZXMgc3VjaCBhcyBgcnR3ZWV0YCwgd2UgY291bGQgZGVzaWduIHRoZSAicGVyZmVjdCBzdGVyZW90eXBlIiBvZiBhIHdpbmUgY29ubm9pc3NldXIgYnkgZ2F0aGVyaW5nIGtleXdvcmRzIGZyb20gdHdlZXRzLiBEZWx2aW5nIGludG8gdGhlIHRleHQgbWluaW5nIHBvcnRpb24gb2YgZGF0YSBzY2llbmNlLCB3ZSBjb3VsZCBmaW5kIHBvcHVsYXIgd29yZHMgdG8gZGVzY3JpYmUgYSB3aW5lIGZyb20gZWFjaCByZXZpZXcgYW5kIGRlc2lnbiBhcnRpZmljaWFsIHJldmlld3MuIFdlIGNvdWxkIHRha2UgdGhlIGRhdGEgd2UgZm91bmQsIGJ1aWxkIHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1vZGVscyBzdWNoIGFzIGEgcmFuZG9tIGZvcmVzdCB0byBwcmVkaWN0IHByaWNlcyBvZiB3aW5lcyBiYXNlZCBvbiB0aGUgZGVzY3JpcHRpb25zOyB0aGlzIHdvdWxkIGJlIGEgdXNlZnVsIG1vZGVsIGZvciBuZXcsIGJ1ZGRpbmcgKHB1biBpbnRlbmRlZCkgd2luZXJpZXMgdG8gZGV0ZXJtaW5lIHRoZSBwcmljZS4gQWRkaXRpb25hbGx5LCB0aGlzIGRhdGEgaXMgb25seSBhIGZyYWN0aW9uIG9mIHRoZSBkYXRhIGZvdW5kIG9uIHRoZSBXaW5lIEVudGh1c2lhc3Qgd2Vic2l0ZS4gT3ZlcmFsbCwgY3JlYXRpdml0eSBpcyB0aGUgb25seSBsaW1pdGF0aW9uIG9uIHdoYXQgb3RoZXIgcHJvYmxlbXMgd2UgY291bGQgc29sdmUgd2l0aCB0aGlzIGRhdGFzZXQuCgpUaGUgZGF0YSB1c2VkIGlzIGp1c3QgYSBmcmFjdGlvbiBvZiB0aGUgZGF0YSBmb3VuZCBvbiB0aGUgV2luZSBFbnRodXNpYXN0IHdlYnNpdGUsIHRoZSB0ZWFtIGhhcyBjcmVhdGVkIGEgc2NyaXB0IHRvIGNvbGxlY3QgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiB0aGUgc2l0ZSwgd2hpY2ggY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vQzRyYnluM200bi93aW5lX3Jldmlld3NfZGF0YV9hbmFseXNpcy90cmVlL21hc3Rlci9kYXRhL3NjcmlwdHMpIGZvciBmdXR1cmUgZXhwYW5zaW9uLgoKIyBEYXRhc2V0cyBDaXRlZApTcGVjaWFsIHRoYW5rcyB0byBbWmFjayBUaG91dHRdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20venluaWNpZGUpIGZvciBwcm92aWRpbmcgdXMgd2l0aCBbZGF0YXNldF8xXShodHRwczovL3d3dy5rYWdnbGUuY29tL3p5bmljaWRlL3dpbmUtcmV2aWV3cykgYW5kIFtTYW55YW0gS2Fwb29yXShodHRwczovL2dpdGh1Yi5jb20vYWN0aXZhdGVkZ2VlaykgZm9yIHByb3ZpZGluZyB1cyB3aXRoICBbZGF0YXNldF8yXShodHRwczovL2dpdGh1Yi5jb20vYWN0aXZhdGVkZ2Vlay93aW5lbWFnLWRhdGFzZXQpLgogClRoZSBkYXRhIHdlIHVzZWQgaXMgYXZhaWxhYmxlIGF0OiBbaHR0cHM6Ly9naXRodWIuY29tL0M0cmJ5bjNtNG4vd2luZV9yZXZpZXdzX2RhdGFfYW5hbHlzaXMvdHJlZS9tYXN0ZXIvZGF0YV0oaHR0cHM6Ly9naXRodWIuY29tL0M0cmJ5bjNtNG4vd2luZV9yZXZpZXdzX2RhdGFfYW5hbHlzaXMvdHJlZS9tYXN0ZXIvZGF0YSk=